作者 景龙

新建独角星球项目

要显示太多修改。

为保证性能只显示 27 of 27+ 个文件。

  1 +.buildpath
  2 +.DS_Store
  3 +.project
  4 +.settings
  5 +.idea
  6 +/api
  7 +/public/api
  8 +/public/assets/dist
  9 +/node_modules
  10 +Vagrantfile
  11 +.vagrant
  1 +如何贡献我的源代码
  2 +===
  1 +ThinkCMF遵循Apache2开源协议发布,并提供免费使用。
  2 +版权所有Copyright © 2013-2018 by ThinkCMF (https://www.thinkcmf.com)
  3 +All rights reserved。
  4 +
  5 +Apache Licence是著名的非盈利开源组织Apache采用的协议。
  6 +该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
  7 +允许代码修改,再作为开源或商业软件发布。需要满足
  8 +的条件:
  9 +1. 需要给代码的用户一份Apache Licence ;
  10 +2. 如果你修改了代码,需要在被修改的文件中说明;
  11 +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
  12 +带有原来代码中的协议,商标,专利声明和其他原来作者规
  13 +定需要包含的说明;
  14 +4. 如果再发布的产品中包含一个Notice文件,则在Notice文
  15 +件中需要带有本协议内容。你可以在Notice中增加自己的
  16 +许可,但不可以表现为对Apache Licence构成更改。
  17 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
  18 +
  19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22 +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23 +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24 +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25 +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  26 +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28 +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  29 +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30 +POSSIBILITY OF SUCH DAMAGE.
  1 +ThinkCMF 5.0.190312 正式版
  2 +===============
  3 +
  4 +> ThinkCMF5.1已经发布,如果你是新同学,请学习[ThinkCMF5.1](https://github.com/thinkcmf/thinkcmf/tree/5.1),更清爽,更自由!
  5 +
  6 +### 系列讲座
  7 +https://www.thinkcmf.com/college.html
  8 +
  9 +### ThinkCMF5主要特性
  10 +* 基于全新 ThinkPHP5.0开发
  11 +* 更规范的代码,遵循PSR-2命名规范和PSR-4自动加载规范
  12 +* 更规范的数据库设计
  13 +* 前后台完全基于bootstrap3
  14 +* 增加 api 模块(需单独下载)
  15 +* 支持 composer 管理第三方库
  16 +* 核心化:独立核心代码包
  17 +* 应用化:开发者以应用的形式增加项目模模块
  18 +* 插件化:更强的插件机制,开发者以插件形式扩展功能
  19 +* 模板化:模板完全傻瓜式,用户无须改动任何代码即可在后台完成模板设计和配置
  20 +* 增加 URL美化功能,支持别名设置,更简单
  21 +* 独立的回收站功能,可以管理所有应用临时删除的数据
  22 +* 统一的资源管理,相同文件只保存一份
  23 +* 注解式的后台菜单管理功能,方便开发者代码管理后台菜单
  24 +* 文件存储插件化,默认支持七牛文件存储插件
  25 +* 模板制作标签化,内置多个cmf标签,方便小白用户
  26 +* 更人性化的导航标签,可以随意定制 html 结构
  27 +* 后台首页插件化,用户可以定制的网站后台首页
  28 +
  29 +### 环境推荐
  30 +> php5.5+
  31 +
  32 +> mysql 5.6+
  33 +
  34 +> 打开rewrite
  35 +
  36 +
  37 +### 最低环境要求
  38 +> php5.4+
  39 +
  40 +> mysql 5.5+ (mysql5.1安装时选择utf8编码,不支持表情符)
  41 +
  42 +> 打开rewrite
  43 +
  44 +
  45 +### 运行环境配置教程
  46 +https://www.thinkcmf.com/topic/1502.html
  47 +
  48 +
  49 +### 自动安装
  50 +> 之前安装过 cmf5的同学,请手动创建`data/install.lock`文件
  51 +
  52 +代码已经加入自动安装程序,如果你在安装中有任何问题请提交 issue!
  53 +
  54 +1. public目录做为网站根目录,入口文件在 public/index.php
  55 +2. 配置好网站,请访问http://你的域名
  56 +
  57 +enjoy your cmf~!
  58 +
  59 +### 系统更新
  60 +如果您是已经安装过 cmf5的用户,请查看 update 目录下的 sql 升级文件,根据自己的下载的程序版本进行更新
  61 +
  62 +### API开发 (支持app,小程序,web)
  63 +如果你需要 `api` 开发请下载:
  64 +ThinkCMF5 API :https://github.com/thinkcmf/thinkcmfapi
  65 +
  66 +### 完整版目录结构
  67 +```
  68 +thinkcmf 根目录
  69 +├─api api目录(核心版不带)
  70 +├─app 应用目录
  71 +│ ├─portal 门户应用目录
  72 +│ │ ├─config.php 应用配置文件
  73 +│ │ ├─common.php 模块函数文件
  74 +│ │ ├─controller 控制器目录
  75 +│ │ ├─model 模型目录
  76 +│ │ └─ ... 更多类库目录
  77 +│ ├─ ... 更多应用
  78 +│ ├─command.php 命令行工具配置文件
  79 +│ ├─common.php 应用公共(函数)文件
  80 +│ ├─config.php 应用(公共)配置文件
  81 +│ ├─database.php 数据库配置文件
  82 +│ ├─tags.php 应用行为扩展定义文件
  83 +│ └─route.php 路由配置文件
  84 +├─data 数据目录
  85 +│ ├─conf 动态配置目录
  86 +│ ├─runtime 应用的运行时目录(可写)
  87 +│ └─ ... 更多
  88 +├─public WEB 部署目录(对外访问目录)
  89 +│ ├─api api入口目录(核心版不带)
  90 +│ ├─plugins 插件目录
  91 +│ ├─static 静态资源存放目录(css,js,image)
  92 +│ ├─themes 前后台主题目录
  93 +│ │ ├─admin_simpleboot3 后台默认主题
  94 +│ │ └─simpleboot3 前台默认主题
  95 +│ ├─upload 文件上传目录
  96 +│ ├─index.php 入口文件
  97 +│ ├─robots.txt 爬虫协议文件
  98 +│ ├─router.php 快速测试文件
  99 +│ └─.htaccess apache重写文件
  100 +├─simplewind
  101 +│ ├─cmf CMF核心库目录
  102 +│ ├─extend 扩展类库目录
  103 +│ ├─thinkphp thinkphp目录
  104 +│ └─vendor 第三方类库目录(Composer)
  105 +├─composer.json composer 定义文件
  106 +├─LICENSE.txt 授权说明文件
  107 +├─README.md README 文件
  108 +├─think 命令行入口文件
  109 +```
  110 +
  111 +### 开发手册
  112 +http://www.kancloud.cn/thinkcmf/doc
  113 +
  114 +### QQ群:
  115 +`ThinkCMF 官方交流群`:316669417
  116 +
  117 +`ThinkCMF 高级交流群`:100828313 (付费)
  118 +高级群专属权益:
  119 +第一波:两个后台风格(ThinkCMF官网风格后台主题,蓝色风格后台主题)
  120 +第二波:ThinkCMF5完全开发手册离线版(PDF,EPUB,MOBI格式)
  121 +更多专属权益正在路上...
  122 +
  123 +`ThinkCMF 铲屎官交流群`:415136742 (生活娱乐,为有喵的猿人准备)
  124 +
  125 +### 话题专区
  126 +http://www.thinkcmf.com/topic/index/index/cat/11.html
  127 +
  128 +### 反馈问题
  129 +https://github.com/thinkcmf/thinkcmf/issues
  130 +
  131 +### 更新日志
  132 +
  133 +#### 5.0.190419
  134 +* 优化动态配置函数
  135 +* 优化门户几处where用法
  136 +* 优化门户后台表单提交方式
  137 +* 优化后台菜单url生成
  138 +* 优化api跨域处理
  139 +* 修复模板设计公共配置文件有数组时报错
  140 +* 修复模板设计公共配置文件里数组列表有数据不显示
  141 +
  142 +#### 5.0.190312
  143 +* 增加`app,api和插件`composer第三方库支持
  144 +* 增加插件`@adminMenuRoot`注解
  145 +* 增加后台模板动态设置
  146 +* 增加`WEB_ROOT`常量
  147 +* 优化url美化可能引起的安全漏洞(漏洞编号CVE-2019-6713 感谢topsec(zhan_ran)的及时反馈)
  148 +* 修复api跨域问题
  149 +* 修复子导航标签报错
  150 +* 计划删除`PLUGINS_PATH`常量,请不要再使用
  151 +* 删除`phpoffice/phpspreadsheet`,`phpoffice/phpexcel`,`dompdf/dompdf`第三方库,请自行安装
  152 +* 移动`qiniu/php-sdk`库到七牛插件
  153 +
  154 +
  155 +#### 5.0.190111
  156 +* 升级ThinkPHP到`5.0.24`(包含安全更新)
  157 +* 增加后台模板按文件列表设计
  158 +* 修复url无法美化
  159 +* 修复页面数据源报错
  160 +
  161 +#### 5.0.181231
  162 +* 规范所有`5.0`代码方便升级到`5.1`
  163 +* 规范控制器`_initialize`方法为`initialize`
  164 +* 调整`cmf_theme_path,cmf_default_theme,cmf_admin_theme_path,cmf_admin_default_theme`到 `template` 配置下
  165 +* `hook,hook_one`方法取消`$extra`参数
  166 +* 增加数据库调试模式开关
  167 +* 增加模板设计关闭功能
  168 +* 优化七牛下载文件名为上传文件名
  169 +* 优化清除缓存,清除opcache缓存
  170 +* 修复七牛获取水印样式报错
  171 +* 修复模板设计后,前台 js 报错
  172 +
  173 +#### 5.0.181212
  174 +* 升级ThinkPHP到`5.0.23`(包含安全更新)
  175 +* 增加`js-bootstrap-year`前端组件
  176 +* 增加文件大小格式化函数`cmf_file_size_format`
  177 +* 修复网站在二级目录下无法设计模板问题
  178 +* 修复模板设计公共模板设置数组无法编辑问题
  179 +* 修复模板设计公共组件数组无法编辑问题
  180 +* 修复门户模板`page.html`报错
  181 +* 优化图片验证码生成
  182 +* 优化`Rest API`跨域问题处理
  183 +
  184 +
  185 +#### 5.0.180901
  186 +* 增强模板设计,提供可视化模板设计
  187 +* 增加模板设计界面钩子
  188 +* 增加验证码图片钩子
  189 +* 增加后台设置网站信息界面钩子
  190 +* 增加后台清除缓存界面钩子
  191 +* 增加后台导航管理界面钩子
  192 +* 增加后台友情链接管理界面钩子
  193 +* 增加后台幻灯片管理界面钩子
  194 +* 增加后台幻灯片页面列表界面钩子
  195 +* 增加后台幻灯片页面添加界面钩子
  196 +* 增加后台幻灯片页面编辑界面钩子
  197 +* 增加后台管理员列表界面钩子
  198 +* 增加后台管理员添加界面钩子
  199 +* 增加后台管理员编辑界面钩子
  200 +* 增加后台角色管理界面钩子
  201 +* 增加后台角色添加界面钩子
  202 +* 增加后台角色编辑界面钩子
  203 +* 增加后台角色授权界面钩子
  204 +* 增加用户管理本站用户列表界面钩子
  205 +* 增加资源管理列表界面钩子
  206 +* 增加用户管理第三方用户列表界面钩子
  207 +* 增加后台首页界面钩子
  208 +* 增加后台回收站界面钩子
  209 +* 增加后台菜单管理界面钩子
  210 +* 增加后台自定义登录是否开启钩子
  211 +* 增加admin.js`js-ajax-btn`组件
  212 +* 优化插件加载
  213 +* 优化前后台上传js
  214 +
  215 +
  216 +[门户应用]
  217 +* 增加文章音频,视频功能
  218 +* 增加门户后台文章管理列表界面钩子
  219 +* 增加门户后台文章添加界面钩子
  220 +* 增加门户后台文章编辑界面钩子
  221 +* 增加门户后台文章分类管理列表界面钩子
  222 +* 增加门户后台文章分类添加界面钩子
  223 +* 增加门户后台文章分类编辑界面钩子
  224 +* 增加门户后台页面管理列表界面钩子
  225 +* 增加门户后台页面添加界面钩子
  226 +* 增加门户后台页面编辑界面钩子
  227 +* 增加门户后台文章标签管理列表界面钩子
  228 +* 增加门户后台文章添加编辑界面右侧栏钩子
  229 +* 增加门户后台文章添加编辑界面主要内容钩子
  230 +* 增加后台文章分类显示隐藏功能
  231 +* 增加后台文章分类列表搜索功能
  232 +* 增加后台文章分类列表层级折叠功能
  233 +
  234 +#### 5.0.180626
  235 +* 升级TP到`5.0.20`
  236 +* 增加插件REST api基类`PluginRestBaseController`
  237 +* 增加我的喜欢功能
  238 +* 增加手机相关设备类型判断函数
  239 +* 优化百度编辑器视频上传
  240 +* 优化get_client_ip()方法,默认使用高级模式
  241 +* 优化手机号检查支持国际手机号
  242 +* 优化图片和文件链接转化函数
  243 +* Restful api基类增加apiVersion属性
  244 +* 修复邮箱验证码发送失败
  245 +* 七牛插件增加东南亚节点
  246 +* 前台模板文件解析标准化
  247 +
  248 +[门户应用]
  249 +* 增加文章`thumbnail`字段
  250 +* 增加文章收藏数功能
  251 +
  252 +#### 5.0.180525
  253 +* 修复ajax请求普通页面时返回格式为json
  254 +* 优化图片链接生成
  255 +* 修复插件模板常量地址问题
  256 +* 修复后台用户注册验证开关错误 #481
  257 +* 增加后台刷新后保持当前页面的功能 #475
  258 +* 取消用户名注册和绑定功能
  259 +* 优化无限滚动jquery插件
  260 +
  261 +#### 5.0.180508
  262 +* 修复用户注册问题
  263 +* 优化缓存清理,防止删除日志文件
  264 +
  265 +#### 5.0.180501
  266 +[核心]
  267 +* 升级TP到5.0.19,增强安全性
  268 +* 修复模板设计数组编辑验证规则不生效 #440
  269 +* 修复后台登录失效后iframe里加载首页
  270 +* 修复七牛插件上传云存储文件没有后缀名问题#437
  271 +* 修复删除评论时的错误文案提示 #443
  272 +* 修复ueditor漏洞#431
  273 +* 修复PHP7.2下后台清除报错
  274 +* 修复前台账号绑定无法获取mobile、email验证码 #418
  275 +* 修复百度编辑器大附件上传问题
  276 +* 修复百度编辑器大视频文件上传问题
  277 +* 升级font awesome到4.7.0
  278 +
  279 +[门户应用]
  280 +* 修复文章分类修改层级后子分类层级不更新问题
  281 +* 优化 portal:articles 标签field,order 属性支持 php 变量
  282 +* 修复文章分类别名设置为纯数字,路由生成错误,无法访问。 #438
  283 +* 增加页面相册和附件功能 #449
  284 +
  285 +#### 5.0.180123
  286 +[核心]
  287 +* 增加小程序管理插件
  288 +* 增加插件后台首页左侧菜单显示
  289 +* 增加 themes 根命名空间
  290 +* 增加模板设计图片模板变量取消功能
  291 +* 增加插件自定义处理配置功能
  292 +* 增加插件后台权限管理功能
  293 +* 增加后台模板切换
  294 +* 增加直传云存储功能
  295 +* 增强导航和子导航标签,自定义更随意
  296 +* 增加before_content,fetch_upload_view,log_write_done,switch_admin_theme钩子
  297 +* 增加PluginAdminBaseController基类
  298 +* 增加系统钩子同步
  299 +* 增加插件中可使用$site_info变量
  300 +* 增加 xml 生成函数
  301 +* 增加插件设置上传文件组件
  302 +* 优化数字验证码日志写入增加过期时间配置
  303 +* 优化数字验证码逻辑,增加数字验证码发送图片验证码,【升级时注意界面逻辑】
  304 +* 优化验证码生成功能,可增加验证码插件管理验证码生成
  305 +* 优化钩子插件管理
  306 +* 优化插件注册机制
  307 +* 优化后台首页菜单加载
  308 +* 修复模板管理变量数据为array时删除出错 #392
  309 +* 修复后台管理搜索翻页时条件丢失问题 #366
  310 +* 修复删除第三方用户时报错 #368
  311 +* 修复在使用cdn加速js时后台文章编辑器时无法加载编辑器配置
  312 +* 修复模板设计模板变量file类型不支持上传 #136
  313 +* 修复用户行为周期设置无效 #382
  314 +* 修复个人信息编辑签名验证问题
  315 +* 修复用户生日早于1970年报错
  316 +* 修复地址坐标选择搜索后无法确定坐标问题
  317 +* 优化IE8,9下的兼容问题
  318 +* 优化前台未登录时跳转方式
  319 +
  320 +[安装程序]
  321 +* 增加安装时管理员密码长度限制 #334
  322 +* 增加安装时检查 rewrite设置
  323 +* 增加安装时 innodb 检测
  324 +* 更正PHP版本要求
  325 +
  326 +[门户应用]
  327 +* 优化portal:articles标签可在模板里设置分页参数和样式
  328 +* 优化portal:articles标签所有属性都支持PHP变量
  329 +* 优化标签控制器支持标签名
  330 +* 增加portal:tagArticles标签
  331 +* 取消文章列表用户关联查询
  332 +* 修复文章多分类进文章列表文章重复问题
  333 +
  334 +
  335 +#### 5.0.170927
  336 +[核心]
  337 +* 增加是否开放注册设置
  338 +* 增加已经安装模板文件检测是否已经删除功能
  339 +* 增加模板卸载风险提示
  340 +* 增加钩子同步功能
  341 +* 增加用户操作同步功能 #291
  342 +* 增加网站信息【$site_info】变量,可以在插件中使用 #310
  343 +* 修复添加管理员不能登录 #110
  344 +* 优化 admin.js
  345 +* 优化后台模板设计排版
  346 +* 优化后台加密码设置
  347 +* 返回按钮统一优化
  348 +* 优化 url 美化时规划选择
  349 +* 修复`api`模块缺少函数报错
  350 +* 修复回收站还原提示错误 #111
  351 +* 修复原始网址和显示网址同时有参数的情况下,两个参数值相同的时候不能解析URL
  352 +* 修复模板设计数组编辑功能缺失
  353 +* 修复后台登录在双核浏览器下会使用 IE 内核问题#168
  354 +* 修复模板widget只有数组时,后台设计保存时报错
  355 +* 修复日期选择在windows firefox下报错
  356 +* 修复模板设计数据源页面清空链接错误
  357 +* 修复后台模板设计,json文件中的数组数据,不能正常显示 #222
  358 +* 修复`cmf\lib\Auth\check`方法逻辑问题 #252
  359 +* 修复后台用户登录自动退出后iframe页跳转到首页的问题
  360 +* 修复用户个人资料修改问题
  361 +* 修复绑定手机号和绑定邮箱号惟一性提示信息错
  362 +
  363 +[安装程序]
  364 +* 更改安装时数据库默认为127.0.0.1
  365 +* 优化安装时链接生成
  366 +
  367 +[门户应用]
  368 +* 增加前台文章控制器默认分类指定
  369 +* 增加后台文章列表所在分类列
  370 +* 增加后台文章分类必须指定分类验证
  371 +* 增加 portal:articles 标签 limit可以设置变量
  372 +* 增加模板设计页面数据源
  373 +* 完善 ApiService获取指定分类下的所有子分类方法
  374 +* 增加portal:categories,portal:subCategories,portal:allSubCategories标签
  375 +* 增加文章、页面、分类模板选择时模板文件名称查看
  376 +* 增加文章保存钩子
  377 +* 优化指定分类下所有子分类获取方法
  378 +* 修复文章分类管理中不保存选择的模板 #107
  379 +* 修复面包屑标签 self属性无法识别 false
  380 +* 修复后台编辑文件会覆盖原作者ID #175
  381 +* 修复后台文章保存后排序变化问题
  382 +* 修复添加文章分类时 path 没数据问题
  383 +
  384 +[升级指导]
  385 +https://www.kancloud.cn/thinkcmf/doc/327443
  386 +
  387 +#### 5.0.170607
  388 +[核心]
  389 +* 删除 app/common.php
  390 +* 规范 admin.js frontend.js函数名
  391 +* 更改后台模板设计的模板文件列表排序规则为从小到大排序
  392 +* 增加模板切换钩子,方便开发者实现复杂的模板切换功能
  393 +* 增加插件作者和演示信息
  394 +* 增加数字验证码模板编辑功能
  395 +* 增加模板变量编辑控件color
  396 +* 增加插件配置组件时间,图片,地理位置,颜色
  397 +* 优化模板配置更新
  398 +* 优化文件上传,检查已经上传文件是否存在,不存在重新上传
  399 +* 修复插件增加新配置时报错
  400 +* 修复模板变量 rule 规则存在,但没有规则时模板设计保存会报错
  401 +* 修复后台清除缓存后url生成不美化
  402 +* 修复模板设计一个页面有多个数组编辑问题
  403 +* 修复cdn设置不生效
  404 +* 修复后台菜单添加子菜单不选择上级问题
  405 +* 修复后台可能多个滚动条
  406 +* 修复后台添加、编辑角色一处文字错误
  407 +* 修复插件更新时不更新新增的钩子
  408 +
  409 +[门户应用]
  410 +* 完善前台模板钩子
  411 +* 完善文章标签功能
  412 +* 增加前台模板手机注册关闭开关
  413 +* 优化文章后台文章分类链接生成
  414 +* 修复ff下文章相册图片替换和删除问题
  415 +* 修复文章分类排序功能
  416 +
  417 +#### 5.0.170520
  418 +[核心]
  419 +* 完善插件后台管理
  420 +* 后台登录插件化
  421 +* 后台首页插件化
  422 +* 文件存储插件化
  423 +* 增加 URL 美化功能
  424 +* 增加后台加密码功能
  425 +* 增加用户修改头像
  426 +* 增加插件设置表单验证
  427 +* 增加前台后台通用语言包
  428 +* 增加编辑器里上传文件链接替换
  429 +* 增加应用 command.php 配置文件
  430 +* 增加后台管理员添加编辑用户名,邮箱惟一性验证
  431 +* 优化安装程序
  432 +* 优化上传文件
  433 +* 优化后台首页
  434 +* 优化回收站
  435 +* 优化插件启用禁用
  436 +* 优化小屏下后台首页不兼容问题
  437 +* 优化后台图片查看
  438 +* 修复后台菜单编辑不生效
  439 +* 修复幻灯片添加不显示问题
  440 +* 修复导航数据源数据返回为空时报错
  441 +* 修复 pathinfo 模式下后台本站用户默认头像不显示问题
  442 +* 修复后台 cdn 不能设置
  443 +* 合并asset应用到 user
  444 +
  445 +[门户应用]
  446 +* 增加文章收藏功能
  447 +* 增加文章点赞限制,一个用户只能点赞一次
  448 +* 增加文章分类缩略图
  449 +* 优化文章分类管理删除
  450 +* 优化文章页和页面页内容图片样式问题
  451 +* 修复文章添加编辑默认图片错误
  452 +* 修复分类下没有文章时报错
  453 +* 修复页面模板设置无效
  454 +* 修复页面删除后仍可以访问
  455 +
  456 +#### 5.0.170505
  457 +[核心]
  458 +* 完善用户注册流程
  459 +* 完善插件功能
  460 +* 增加手机验证码发送钩子
  461 +* 增加手机验证码发送演示插件
  462 +* 增加用户邮箱绑定
  463 +* 增加用户手机绑定
  464 +* 增加常用模板钩子
  465 +* 增加模板设计图片上传
  466 +* 增加用户密码修改
  467 +* 增加用户收藏功能
  468 +* 增加导航标签,子导航标签增加 `max-level` 设置
  469 +* 修复邮箱验证码发送
  470 +* 修复windows下获取模板数据时模板文件路径问题
  471 +* 修复单文件,多文件上传
  472 +* 修复后台首页用户昵称一直显示admin
  473 +* 修复 `navigation`,`subNavigation` 标签两个以上不能同时使用问题
  474 +* 修复 console 模式报错
  475 +* 取消前台有错误时界面刷新
  476 +
  477 +[门户应用]
  478 +* 增加文章附件功能
  479 +* 优化文章相册
  480 +
  481 +#### 5.0.170422
  482 +[核心]
  483 +* 完善幻灯片
  484 +* 完善后台控制器方法注释
  485 +* 增加调试模式下实时更新模板配置
  486 +* 增加友情链接图片上传
  487 +* 增加应用公共语言包功能
  488 +* 增加资源管理
  489 +* 增加模板设计数据源层级关系
  490 +* 更新jQuery Form版本
  491 +* 增加后台菜单类型是否有界面区分
  492 +* 增加权限验证时权限规则里没有的规则不用验证
  493 +* 增加前台网站信息获取
  494 +* 优化后台菜单导入
  495 +* 统一排序规则,按从小到大排序
  496 +* 修复后台模板管理点更新提示卸载
  497 +* 修复标签`NavigationMenu`
  498 +* 修复菜单导入时未添加权限规则
  499 +* 修复`navigationFolder`设置多个子菜单后会多循环数据
  500 +* 修复部分代码php5.4下不兼容
  501 +* 修复后台菜单不能添加编辑
  502 +
  503 +[门户应用]
  504 +* 完全独立门户应用
  505 +* 完善后台页面管理
  506 +* 完善面包屑标签`breadcrumb`
  507 +* 完善文章分类管理
  508 +* 完善文章管理
  509 +* 修复文章分类`path`更新
  510 +* 优化文章列表标签`articles`
  511 +* 优化后台文章分类选择
  512 +* 增加前台文章点赞功能
  513 +* 增加前台文章搜索功能
  514 +* 增加文章列表分页总数获取
  515 +
  516 +#### 5.0.170401
  517 +* 完善文件上传
  518 +* 增加回收站功能
  519 +* 完善友情链接
  520 +* 优化网站设置
  521 +* 增加后台登陆验证码
  522 +* 修复后台用户密码修改
  523 +* 修复用户管理-本站用户头像不显示
  524 +* 完善前台用户登录注册
  525 +* 增加后台菜单导入
  526 +* 修复后台菜单列表排序
  527 +* 完善导航
  528 +* 增加插件钩子管理
  529 +* 完善前台模板
  530 +
  531 +
  1 +theme: jekyll-theme-cayman
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <zxxjjforever@163.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\annotation;
  12 +
  13 +use mindplay\annotations\Annotation;
  14 +
  15 +/**
  16 + * Specifies validation of a string, requiring a minimum and/or maximum length.
  17 + *
  18 + * @usage('method'=>true, 'inherited'=>true, 'multiple'=>false)
  19 + */
  20 +class AdminMenuAnnotation extends Annotation
  21 +{
  22 + public $remark = '';
  23 +
  24 + public $icon = '';
  25 +
  26 + public $name = '';
  27 +
  28 + public $param = '';
  29 +
  30 + public $parent = '';
  31 +
  32 + public $display = false;
  33 +
  34 + public $order = 10000;
  35 +
  36 + public $hasView = true;
  37 +
  38 + /**
  39 + * Initialize the annotation.
  40 + * @param array $properties
  41 + */
  42 + public function initAnnotation(array $properties)
  43 + {
  44 + parent::initAnnotation($properties);
  45 + }
  46 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <zxxjjforever@163.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\annotation;
  12 +
  13 +use mindplay\annotations\AnnotationException;
  14 +use mindplay\annotations\Annotation;
  15 +
  16 +/**
  17 + * Specifies validation of a string, requiring a minimum and/or maximum length.
  18 + *
  19 + * @usage('class'=>true, 'inherited'=>true, 'multiple'=>true)
  20 + */
  21 +class AdminMenuRootAnnotation extends Annotation
  22 +{
  23 + /**
  24 + * @var int|null Minimum string length (or null, if no minimum)
  25 + */
  26 + public $remark = '';
  27 +
  28 + /**
  29 + * @var int|null Maximum string length (or null, if no maximum)
  30 + */
  31 + public $icon = '';
  32 +
  33 + /**
  34 + * @var int|null Minimum string length (or null, if no minimum)
  35 + */
  36 + public $name = '';
  37 +
  38 + public $action = '';
  39 +
  40 + public $param = '';
  41 +
  42 + public $parent = '';
  43 +
  44 + public $display = false;
  45 +
  46 + public $order = 10000;
  47 +
  48 + /**
  49 + * Initialize the annotation.
  50 + * @param array $properties
  51 + */
  52 + public function initAnnotation(array $properties)
  53 + {
  54 + parent::initAnnotation($properties);
  55 + }
  56 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <thinkcmf@126.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\api;
  12 +
  13 +use app\admin\model\NavModel;
  14 +use think\db\Query;
  15 +
  16 +class NavApi
  17 +{
  18 + /**
  19 + * 导航模板数据源 用于模板设计
  20 + * @param array $param
  21 + * @return array|\PDOStatement|string|\think\Collection
  22 + * @throws \think\db\exception\DataNotFoundException
  23 + * @throws \think\db\exception\ModelNotFoundException
  24 + * @throws \think\exception\DbException
  25 + */
  26 + public function index($param = [])
  27 + {
  28 + $navModel = new NavModel();
  29 +
  30 + $result = $navModel
  31 + ->where(function (Query $query) use ($param) {
  32 + if (!empty($param['keyword'])) {
  33 + $query->where('name', 'like', "%{$param['keyword']}%");
  34 + }
  35 + })->select();
  36 +
  37 + return $result;
  38 + }
  39 +
  40 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <thinkcmf@126.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\api;
  12 +
  13 +use app\admin\model\NavMenuModel;
  14 +
  15 +class NavMenuApi
  16 +{
  17 + /**
  18 + * 导航菜单模板数据源 用于模板设计
  19 + * @param array $param
  20 + * @return array|\PDOStatement|string|\think\Collection
  21 + * @throws \think\db\exception\DataNotFoundException
  22 + * @throws \think\db\exception\ModelNotFoundException
  23 + * @throws \think\exception\DbException
  24 + */
  25 + public function index($param = [])
  26 + {
  27 + $navMenuModel = new NavMenuModel();
  28 +
  29 + $result = $navMenuModel
  30 + ->where(function (Query $query) use ($param) {
  31 + if (!empty($param['keyword'])) {
  32 + $query->where('name', 'like', "%{$param['keyword']}%");
  33 + }
  34 +
  35 + if (!empty($param['id'])) {
  36 + $query->where('nav_id', intval($param['id']));
  37 + }
  38 + })->select();
  39 +
  40 + return $result;
  41 + }
  42 +
  43 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <thinkcmf@126.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\api;
  12 +
  13 +use app\admin\model\SlideModel;
  14 +use think\db\Query;
  15 +
  16 +class SlideApi
  17 +{
  18 + /**
  19 + * 幻灯片模板数据源 用于模板设计
  20 + * @param array $param
  21 + * @return false|\PDOStatement|string|\think\Collection
  22 + * @throws \think\db\exception\DataNotFoundException
  23 + * @throws \think\db\exception\ModelNotFoundException
  24 + * @throws \think\exception\DbException
  25 + */
  26 + public function index($param = [])
  27 + {
  28 + $slideModel = new SlideModel();
  29 +
  30 + //返回的数据必须是数据集或数组,item里必须包括id,name,如果想表示层级关系请加上 parent_id
  31 + return $slideModel
  32 + ->where(function (Query $query) use ($param) {
  33 + if (!empty($param['keyword'])) {
  34 + $query->where('name', 'like', "%{$param['keyword']}%");
  35 + }
  36 + })->select();
  37 + }
  38 +
  39 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\AboutUsModel;
  15 +use app\portal\model\PortalPostModel;
  16 +
  17 +class AboutUsController extends AdminBaseController
  18 +{
  19 + protected $targets = ["_blank" => "新标签页打开", "_self" => "本窗口打开"];
  20 +
  21 + //编辑页面
  22 + public function edit(){
  23 + $AboutUsModel = new AboutUsModel();
  24 + $about = $AboutUsModel->where('id',1)->find();
  25 + $contentModel = new PortalPostModel();
  26 + $about['content'] = $contentModel->getPostContentAttr($about['content']);
  27 + $this->assign('post', $about);
  28 + return $this->fetch();
  29 + }
  30 +
  31 + //编辑保存页面
  32 + public function editPost(){
  33 + $data = $this->request->param();
  34 + $AboutUsModel = new AboutUsModel();
  35 + $result = $this->validate($data, 'AboutUs');
  36 + if ($result !== true) {
  37 + $this->error($result);
  38 + }
  39 + $contentModel = new PortalPostModel();
  40 + $data['content'] = $contentModel->setPostContentAttr($data['content']);
  41 + $AboutUsModel->allowField(true)->isUpdate(true)->save($data);
  42 +
  43 + $this->success("保存成功!");
  44 + }
  45 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\CooperationModel;
  15 +
  16 +class CooperationController extends AdminBaseController
  17 +{
  18 + protected $targets = ["_blank" => "新标签页打开", "_self" => "本窗口打开"];
  19 +
  20 + //首页
  21 + public function index(){
  22 + $CooperationModel = new CooperationModel();
  23 + $cooperation = $CooperationModel->order('weigh desc')->select();
  24 + $this->assign('cooperation', $cooperation);
  25 + return $this->fetch();
  26 + }
  27 +
  28 + //添加页面
  29 + public function add(){
  30 + $this->assign('targets', $this->targets);
  31 + return $this->fetch();
  32 + }
  33 +
  34 + //提交保存
  35 + public function addPost(){
  36 + $data = $this->request->param();
  37 + $CooperationModel = new CooperationModel();
  38 + $result = $this->validate($data, 'Cooperation');
  39 + if ($result !== true) {
  40 + $this->error($result);
  41 + }
  42 + $CooperationModel->allowField(true)->save($data);
  43 +
  44 + $this->success("添加成功!", url("Cooperation/index"));
  45 + }
  46 +
  47 + //编辑页面
  48 + public function edit(){
  49 + $id = $this->request->param('id', 0, 'intval');
  50 + $CooperationModel = new CooperationModel();
  51 + $cooperation = $CooperationModel->get($id);
  52 + $this->assign('targets', $this->targets);
  53 + $this->assign('cooperation', $cooperation);
  54 + return $this->fetch();
  55 + }
  56 +
  57 + //编辑保存页面
  58 + public function editPost()
  59 + {
  60 + $data = $this->request->param();
  61 + $CooperationModel = new CooperationModel();
  62 + $result = $this->validate($data, 'Cooperation');
  63 + if ($result !== true) {
  64 + $this->error($result);
  65 + }
  66 + $CooperationModel->allowField(true)->isUpdate(true)->save($data);
  67 +
  68 + $this->success("保存成功!", url("Cooperation/index"));
  69 + }
  70 +
  71 + //删除
  72 + public function delete(){
  73 + $id = $this->request->param('id', 0, 'intval');
  74 + CooperationModel::destroy($id);
  75 + $this->success("删除成功!", url("Cooperation/index"));
  76 + }
  77 +
  78 + //显示与隐藏
  79 + public function toggle()
  80 + {
  81 + $data = $this->request->param();
  82 + $CooperationModel = new CooperationModel();
  83 +
  84 + if (isset($data['ids']) && !empty($data["display"])) {
  85 + $ids = $this->request->param('ids/a');
  86 + $CooperationModel->where('id', 'in', $ids)->update(['status' => 1]);
  87 + $this->success("更新成功!");
  88 + }
  89 +
  90 + if (isset($data['ids']) && !empty($data["hide"])) {
  91 + $ids = $this->request->param('ids/a');
  92 + $CooperationModel->where('id', 'in', $ids)->update(['status' => 0]);
  93 + $this->success("更新成功!");
  94 + }
  95 +
  96 + }
  97 +
  98 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <zxxjjforever@163.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +
  15 +class DialogController extends AdminBaseController
  16 +{
  17 + public function initialize()
  18 + {
  19 +
  20 + }
  21 +
  22 + public function map()
  23 + {
  24 + $location = $this->request->param('location');
  25 + $location = explode(',', $location);
  26 + $lng = empty($location[0]) ? 116.424966 : $location[0];
  27 + $lat = empty($location[1]) ? 39.907851 : $location[1];
  28 +
  29 + $this->assign(['lng' => $lng, 'lat' => $lat]);
  30 + return $this->fetch();
  31 + }
  32 +
  33 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\FocusModel;
  15 +
  16 +class FocusController extends AdminBaseController
  17 +{
  18 + protected $targets = ["_blank" => "新标签页打开", "_self" => "本窗口打开"];
  19 +
  20 + //首页
  21 + public function index(){
  22 + $FocusModel = new FocusModel();
  23 + $Focus = $FocusModel->order('weigh desc')->select();
  24 + $this->assign('focus', $Focus);
  25 + return $this->fetch();
  26 + }
  27 +
  28 + //添加页面
  29 + public function add(){
  30 + $this->assign('targets', $this->targets);
  31 + return $this->fetch();
  32 + }
  33 +
  34 + //提交保存
  35 + public function addPost(){
  36 + $data = $this->request->param();
  37 + $FocusModel = new FocusModel();
  38 + $result = $this->validate($data, 'Focus');
  39 + if ($result !== true) {
  40 + $this->error($result);
  41 + }
  42 + $FocusModel->allowField(true)->save($data);
  43 +
  44 + $this->success("添加成功!", url("Focus/index"));
  45 + }
  46 +
  47 + //编辑页面
  48 + public function edit(){
  49 + $id = $this->request->param('id', 0, 'intval');
  50 + $FocusModel = new FocusModel();
  51 + $Focus = $FocusModel->get($id);
  52 + $this->assign('targets', $this->targets);
  53 + $this->assign('focus', $Focus);
  54 + return $this->fetch();
  55 + }
  56 +
  57 + //编辑保存页面
  58 + public function editPost()
  59 + {
  60 + $data = $this->request->param();
  61 + $FocusModel = new FocusModel();
  62 + $result = $this->validate($data, 'Focus');
  63 + if ($result !== true) {
  64 + $this->error($result);
  65 + }
  66 + $FocusModel->allowField(true)->isUpdate(true)->save($data);
  67 +
  68 + $this->success("保存成功!", url("Focus/index"));
  69 + }
  70 +
  71 + //删除
  72 + public function delete(){
  73 + $id = $this->request->param('id', 0, 'intval');
  74 + FocusModel::destroy($id);
  75 + $this->success("删除成功!", url("Focus/index"));
  76 + }
  77 +
  78 + //显示与隐藏
  79 + public function toggle()
  80 + {
  81 + $data = $this->request->param();
  82 + $FocusModel = new FocusModel();
  83 +
  84 + if (isset($data['ids']) && !empty($data["display"])) {
  85 + $ids = $this->request->param('ids/a');
  86 + $FocusModel->where('id', 'in', $ids)->update(['status' => 1]);
  87 + $this->success("更新成功!");
  88 + }
  89 +
  90 + if (isset($data['ids']) && !empty($data["hide"])) {
  91 + $ids = $this->request->param('ids/a');
  92 + $FocusModel->where('id', 'in', $ids)->update(['status' => 0]);
  93 + $this->success("更新成功!");
  94 + }
  95 +
  96 + }
  97 +
  98 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <zxxjjforever@163.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\HookModel;
  15 +use app\admin\model\PluginModel;
  16 +use app\admin\model\HookPluginModel;
  17 +use think\Db;
  18 +
  19 +/**
  20 + * Class HookController 钩子管理控制器
  21 + * @package app\admin\controller
  22 + */
  23 +class HookController extends AdminBaseController
  24 +{
  25 + /**
  26 + * 钩子管理
  27 + * @adminMenu(
  28 + * 'name' => '钩子管理',
  29 + * 'parent' => 'admin/Plugin/default',
  30 + * 'display'=> true,
  31 + * 'hasView'=> true,
  32 + * 'order' => 10000,
  33 + * 'icon' => '',
  34 + * 'remark' => '钩子管理',
  35 + * 'param' => ''
  36 + * )
  37 + */
  38 + public function index()
  39 + {
  40 + $hookModel = new HookModel();
  41 + $hooks = $hookModel->select();
  42 + $this->assign('hooks', $hooks);
  43 + return $this->fetch();
  44 + }
  45 +
  46 + /**
  47 + * 钩子插件管理
  48 + * @adminMenu(
  49 + * 'name' => '钩子插件管理',
  50 + * 'parent' => 'index',
  51 + * 'display'=> false,
  52 + * 'hasView'=> true,
  53 + * 'order' => 10000,
  54 + * 'icon' => '',
  55 + * 'remark' => '钩子插件管理',
  56 + * 'param' => ''
  57 + * )
  58 + */
  59 + public function plugins()
  60 + {
  61 + $hook = $this->request->param('hook');
  62 + $pluginModel = new PluginModel();
  63 + $plugins = $pluginModel
  64 + ->field('a.*,b.hook,b.plugin,b.list_order,b.status as hook_plugin_status,b.id as hook_plugin_id')
  65 + ->alias('a')
  66 + ->join('__HOOK_PLUGIN__ b', 'a.name = b.plugin')
  67 + ->where('b.hook', $hook)
  68 + ->order('b.list_order asc')
  69 + ->select();
  70 + $this->assign('plugins', $plugins);
  71 + return $this->fetch();
  72 + }
  73 +
  74 + /**
  75 + * 钩子插件排序
  76 + * @adminMenu(
  77 + * 'name' => '钩子插件排序',
  78 + * 'parent' => 'index',
  79 + * 'display'=> false,
  80 + * 'hasView'=> false,
  81 + * 'order' => 10000,
  82 + * 'icon' => '',
  83 + * 'remark' => '钩子插件排序',
  84 + * 'param' => ''
  85 + * )
  86 + */
  87 + public function pluginListOrder()
  88 + {
  89 + $hookPluginModel = new HookPluginModel();
  90 + parent::listOrders($hookPluginModel);
  91 +
  92 + $this->success("排序更新成功!");
  93 + }
  94 +
  95 + /**
  96 + * 同步钩子
  97 + * @adminMenu(
  98 + * 'name' => '同步钩子',
  99 + * 'parent' => 'index',
  100 + * 'display'=> false,
  101 + * 'hasView'=> true,
  102 + * 'order' => 10000,
  103 + * 'icon' => '',
  104 + * 'remark' => '同步钩子',
  105 + * 'param' => ''
  106 + * )
  107 + */
  108 + public function sync()
  109 + {
  110 +
  111 + $apps = cmf_scan_dir(APP_PATH . '*', GLOB_ONLYDIR);
  112 +
  113 + array_push($apps, 'cmf');
  114 +
  115 + foreach ($apps as $app) {
  116 + if ($app == 'cmf') {
  117 + $hookConfigFile = cmf_core_path() . 'hooks.php';
  118 + } else {
  119 + $hookConfigFile = APP_PATH . $app . '/hooks.php';
  120 + }
  121 +
  122 + if (file_exists($hookConfigFile)) {
  123 + $hooksInFile = include $hookConfigFile;
  124 +
  125 + foreach ($hooksInFile as $hookName => $hook) {
  126 +
  127 + $hook['type'] = empty($hook['type']) ? 2 : $hook['type'];
  128 +
  129 + if (!in_array($hook['type'], [2, 3, 4]) && $app != 'cmf') {
  130 + $hook['type'] = 2;
  131 + }
  132 +
  133 + $findHook = Db::name('hook')->where('hook', $hookName)->count();
  134 +
  135 + $hook['app'] = $app;
  136 +
  137 + if ($findHook > 0) {
  138 + Db::name('hook')->where('hook', $hookName)->strict(false)->field(true)->update($hook);
  139 + } else {
  140 + $hook['hook'] = $hookName;
  141 + Db::name('hook')->insert($hook);
  142 + }
  143 + }
  144 + }
  145 + }
  146 +
  147 + return $this->fetch();
  148 + }
  149 +
  150 +
  151 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use think\Db;
  15 +use app\admin\model\AdminMenuModel;
  16 +
  17 +class IndexController extends AdminBaseController
  18 +{
  19 +
  20 + public function initialize()
  21 + {
  22 + $adminSettings = cmf_get_option('admin_settings');
  23 + if (empty($adminSettings['admin_password']) || $this->request->path() == $adminSettings['admin_password']) {
  24 + $adminId = cmf_get_current_admin_id();
  25 + if (empty($adminId)) {
  26 + session("__LOGIN_BY_CMF_ADMIN_PW__", 1);//设置后台登录加密码
  27 + }
  28 + }
  29 +
  30 + parent::initialize();
  31 + }
  32 +
  33 + /**
  34 + * 后台首页
  35 + */
  36 + public function index()
  37 + {
  38 + $content = hook_one('admin_index_index_view');
  39 +
  40 + if (!empty($content)) {
  41 + return $content;
  42 + }
  43 +
  44 + $adminMenuModel = new AdminMenuModel();
  45 + $menus = cache('admin_menus_' . cmf_get_current_admin_id(), '', null, 'admin_menus');
  46 +
  47 + if (empty($menus)) {
  48 + $menus = $adminMenuModel->menuTree();
  49 + cache('admin_menus_' . cmf_get_current_admin_id(), $menus, null, 'admin_menus');
  50 + }
  51 +
  52 + $this->assign("menus", $menus);
  53 +
  54 +
  55 + $result = Db::name('AdminMenu')->order(["app" => "ASC", "controller" => "ASC", "action" => "ASC"])->select();
  56 + $menusTmp = array();
  57 + foreach ($result as $item){
  58 + //去掉/ _ 全部小写。作为索引。
  59 + $indexTmp = $item['app'].$item['controller'].$item['action'];
  60 + $indexTmp = preg_replace("/[\\/|_]/","",$indexTmp);
  61 + $indexTmp = strtolower($indexTmp);
  62 + $menusTmp[$indexTmp] = $item;
  63 + }
  64 + $this->assign("menus_js_var",json_encode($menusTmp));
  65 +
  66 + //$admin = Db::name("user")->where('id', cmf_get_current_admin_id())->find();
  67 + //$this->assign('admin', $admin);
  68 + return $this->fetch();
  69 + }
  70 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\LinkModel;
  15 +
  16 +class LinkController extends AdminBaseController
  17 +{
  18 + protected $targets = ["_blank" => "新标签页打开", "_self" => "本窗口打开"];
  19 +
  20 + /**
  21 + * 友情链接管理
  22 + * @adminMenu(
  23 + * 'name' => '友情链接',
  24 + * 'parent' => 'admin/Setting/default',
  25 + * 'display'=> true,
  26 + * 'hasView'=> true,
  27 + * 'order' => 50,
  28 + * 'icon' => '',
  29 + * 'remark' => '友情链接管理',
  30 + * 'param' => ''
  31 + * )
  32 + * @return mixed
  33 + * @throws \think\db\exception\DataNotFoundException
  34 + * @throws \think\db\exception\ModelNotFoundException
  35 + * @throws \think\exception\DbException
  36 + */
  37 + public function index()
  38 + {
  39 + $content = hook_one('admin_link_index_view');
  40 +
  41 + if (!empty($content)) {
  42 + return $content;
  43 + }
  44 +
  45 + $linkModel = new LinkModel();
  46 + $links = $linkModel->select();
  47 + $this->assign('links', $links);
  48 +
  49 + return $this->fetch();
  50 + }
  51 +
  52 + /**
  53 + * 添加友情链接
  54 + * @adminMenu(
  55 + * 'name' => '添加友情链接',
  56 + * 'parent' => 'index',
  57 + * 'display'=> false,
  58 + * 'hasView'=> true,
  59 + * 'order' => 10000,
  60 + * 'icon' => '',
  61 + * 'remark' => '添加友情链接',
  62 + * 'param' => ''
  63 + * )
  64 + */
  65 + public function add()
  66 + {
  67 + $this->assign('targets', $this->targets);
  68 + return $this->fetch();
  69 + }
  70 +
  71 + /**
  72 + * 添加友情链接提交保存
  73 + * @adminMenu(
  74 + * 'name' => '添加友情链接提交保存',
  75 + * 'parent' => 'index',
  76 + * 'display'=> false,
  77 + * 'hasView'=> false,
  78 + * 'order' => 10000,
  79 + * 'icon' => '',
  80 + * 'remark' => '添加友情链接提交保存',
  81 + * 'param' => ''
  82 + * )
  83 + */
  84 + public function addPost()
  85 + {
  86 + $data = $this->request->param();
  87 + $linkModel = new LinkModel();
  88 + $result = $this->validate($data, 'Link');
  89 + if ($result !== true) {
  90 + $this->error($result);
  91 + }
  92 + $linkModel->allowField(true)->save($data);
  93 +
  94 + $this->success("添加成功!", url("Link/index"));
  95 + }
  96 +
  97 + /**
  98 + * 编辑友情链接
  99 + * @adminMenu(
  100 + * 'name' => '编辑友情链接',
  101 + * 'parent' => 'index',
  102 + * 'display'=> false,
  103 + * 'hasView'=> true,
  104 + * 'order' => 10000,
  105 + * 'icon' => '',
  106 + * 'remark' => '编辑友情链接',
  107 + * 'param' => ''
  108 + * )
  109 + * @return mixed
  110 + * @throws \think\Exception\DbException
  111 + */
  112 + public function edit()
  113 + {
  114 + $id = $this->request->param('id', 0, 'intval');
  115 + $linkModel = new LinkModel();
  116 + $link = $linkModel->get($id);
  117 + $this->assign('targets', $this->targets);
  118 + $this->assign('link', $link);
  119 + return $this->fetch();
  120 + }
  121 +
  122 + /**
  123 + * 编辑友情链接提交保存
  124 + * @adminMenu(
  125 + * 'name' => '编辑友情链接提交保存',
  126 + * 'parent' => 'index',
  127 + * 'display'=> false,
  128 + * 'hasView'=> false,
  129 + * 'order' => 10000,
  130 + * 'icon' => '',
  131 + * 'remark' => '编辑友情链接提交保存',
  132 + * 'param' => ''
  133 + * )
  134 + */
  135 + public function editPost()
  136 + {
  137 + $data = $this->request->param();
  138 + $linkModel = new LinkModel();
  139 + $result = $this->validate($data, 'Link');
  140 + if ($result !== true) {
  141 + $this->error($result);
  142 + }
  143 + $linkModel->allowField(true)->isUpdate(true)->save($data);
  144 +
  145 + $this->success("保存成功!", url("Link/index"));
  146 + }
  147 +
  148 + /**
  149 + * 删除友情链接
  150 + * @adminMenu(
  151 + * 'name' => '删除友情链接',
  152 + * 'parent' => 'index',
  153 + * 'display'=> false,
  154 + * 'hasView'=> false,
  155 + * 'order' => 10000,
  156 + * 'icon' => '',
  157 + * 'remark' => '删除友情链接',
  158 + * 'param' => ''
  159 + * )
  160 + */
  161 + public function delete()
  162 + {
  163 + $id = $this->request->param('id', 0, 'intval');
  164 + LinkModel::destroy($id);
  165 + $this->success("删除成功!", url("link/index"));
  166 + }
  167 +
  168 + /**
  169 + * 友情链接排序
  170 + * @adminMenu(
  171 + * 'name' => '友情链接排序',
  172 + * 'parent' => 'index',
  173 + * 'display'=> false,
  174 + * 'hasView'=> false,
  175 + * 'order' => 10000,
  176 + * 'icon' => '',
  177 + * 'remark' => '友情链接排序',
  178 + * 'param' => ''
  179 + * )
  180 + */
  181 + public function listOrder()
  182 + {
  183 + $linkModel = new LinkModel();
  184 + parent::listOrders($linkModel);
  185 + $this->success("排序更新成功!");
  186 + }
  187 +
  188 + /**
  189 + * 友情链接显示隐藏
  190 + * @adminMenu(
  191 + * 'name' => '友情链接显示隐藏',
  192 + * 'parent' => 'index',
  193 + * 'display'=> false,
  194 + * 'hasView'=> false,
  195 + * 'order' => 10000,
  196 + * 'icon' => '',
  197 + * 'remark' => '友情链接显示隐藏',
  198 + * 'param' => ''
  199 + * )
  200 + */
  201 + public function toggle()
  202 + {
  203 + $data = $this->request->param();
  204 + $linkModel = new LinkModel();
  205 +
  206 + if (isset($data['ids']) && !empty($data["display"])) {
  207 + $ids = $this->request->param('ids/a');
  208 + $linkModel->where('id', 'in', $ids)->update(['status' => 1]);
  209 + $this->success("更新成功!");
  210 + }
  211 +
  212 + if (isset($data['ids']) && !empty($data["hide"])) {
  213 + $ids = $this->request->param('ids/a');
  214 + $linkModel->where('id', 'in', $ids)->update(['status' => 0]);
  215 + $this->success("更新成功!");
  216 + }
  217 +
  218 +
  219 + }
  220 +
  221 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use think\Validate;
  15 +
  16 +class MailerController extends AdminBaseController
  17 +{
  18 +
  19 + /**
  20 + * 邮箱配置
  21 + * @adminMenu(
  22 + * 'name' => '邮箱配置',
  23 + * 'parent' => 'admin/Setting/default',
  24 + * 'display'=> true,
  25 + * 'hasView'=> true,
  26 + * 'order' => 10,
  27 + * 'icon' => '',
  28 + * 'remark' => '邮箱配置',
  29 + * 'param' => ''
  30 + * )
  31 + */
  32 + public function index()
  33 + {
  34 + $emailSetting = cmf_get_option('smtp_setting');
  35 + $this->assign($emailSetting);
  36 + return $this->fetch();
  37 + }
  38 +
  39 + /**
  40 + * 邮箱配置
  41 + * @adminMenu(
  42 + * 'name' => '邮箱配置提交保存',
  43 + * 'parent' => 'index',
  44 + * 'display'=> false,
  45 + * 'hasView'=> false,
  46 + * 'order' => 10000,
  47 + * 'icon' => '',
  48 + * 'remark' => '邮箱配置提交保存',
  49 + * 'param' => ''
  50 + * )
  51 + */
  52 + public function indexPost()
  53 + {
  54 + $post = array_map('trim', $this->request->param());
  55 +
  56 + if (in_array('', $post) && !empty($post['smtpsecure'])) {
  57 + $this->error("不能留空!");
  58 + }
  59 +
  60 + cmf_set_option('smtp_setting', $post);
  61 +
  62 + $this->success("保存成功!");
  63 + }
  64 +
  65 + /**
  66 + * 邮件模板
  67 + * @adminMenu(
  68 + * 'name' => '邮件模板',
  69 + * 'parent' => 'index',
  70 + * 'display'=> false,
  71 + * 'hasView'=> true,
  72 + * 'order' => 10000,
  73 + * 'icon' => '',
  74 + * 'remark' => '邮件模板',
  75 + * 'param' => ''
  76 + * )
  77 + */
  78 + public function template()
  79 + {
  80 + $allowedTemplateKeys = ['verification_code'];
  81 + $templateKey = $this->request->param('template_key');
  82 +
  83 + if (empty($templateKey) || !in_array($templateKey, $allowedTemplateKeys)) {
  84 + $this->error('非法请求!');
  85 + }
  86 +
  87 + $template = cmf_get_option('email_template_' . $templateKey);
  88 + $this->assign($template);
  89 + return $this->fetch('template_verification_code');
  90 + }
  91 +
  92 + /**
  93 + * 邮件模板提交
  94 + * @adminMenu(
  95 + * 'name' => '邮件模板提交',
  96 + * 'parent' => 'index',
  97 + * 'display'=> false,
  98 + * 'hasView'=> false,
  99 + * 'order' => 10000,
  100 + * 'icon' => '',
  101 + * 'remark' => '邮件模板提交',
  102 + * 'param' => ''
  103 + * )
  104 + */
  105 + public function templatePost()
  106 + {
  107 + $allowedTemplateKeys = ['verification_code'];
  108 + $templateKey = $this->request->param('template_key');
  109 +
  110 + if (empty($templateKey) || !in_array($templateKey, $allowedTemplateKeys)) {
  111 + $this->error('非法请求!');
  112 + }
  113 +
  114 + $data = $this->request->param();
  115 +
  116 + unset($data['template_key']);
  117 +
  118 + cmf_set_option('email_template_' . $templateKey, $data);
  119 +
  120 + $this->success("保存成功!");
  121 + }
  122 +
  123 + /**
  124 + * 邮件发送测试
  125 + * @adminMenu(
  126 + * 'name' => '邮件发送测试',
  127 + * 'parent' => 'index',
  128 + * 'display'=> false,
  129 + * 'hasView'=> true,
  130 + * 'order' => 10000,
  131 + * 'icon' => '',
  132 + * 'remark' => '邮件发送测试',
  133 + * 'param' => ''
  134 + * )
  135 + */
  136 + public function test()
  137 + {
  138 + if ($this->request->isPost()) {
  139 +
  140 + $validate = new Validate([
  141 + 'to' => 'require|email',
  142 + 'subject' => 'require',
  143 + 'content' => 'require',
  144 + ]);
  145 + $validate->message([
  146 + 'to.require' => '收件箱不能为空!',
  147 + 'to.email' => '收件箱格式不正确!',
  148 + 'subject.require' => '标题不能为空!',
  149 + 'content.require' => '内容不能为空!',
  150 + ]);
  151 +
  152 + $data = $this->request->param();
  153 + if (!$validate->check($data)) {
  154 + $this->error($validate->getError());
  155 + }
  156 +
  157 + $result = cmf_send_email($data['to'], $data['subject'], $data['content']);
  158 + if ($result && empty($result['error'])) {
  159 + $this->success('发送成功!');
  160 + } else {
  161 + $this->error('发送失败:' . $result['message']);
  162 + }
  163 +
  164 + } else {
  165 + return $this->fetch();
  166 + }
  167 +
  168 + }
  169 +
  170 +
  171 +}
  172 +
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use think\Db;
  15 +use app\admin\model\Menu;
  16 +
  17 +class MainController extends AdminBaseController
  18 +{
  19 +
  20 + /**
  21 + * 后台欢迎页
  22 + */
  23 + public function index()
  24 + {
  25 + $dashboardWidgets = [];
  26 + $widgets = cmf_get_option('admin_dashboard_widgets');
  27 +
  28 + $defaultDashboardWidgets = [
  29 + '_SystemCmfHub' => ['name' => 'CmfHub', 'is_system' => 1],
  30 + '_SystemCmfDocuments' => ['name' => 'CmfDocuments', 'is_system' => 1],
  31 + '_SystemMainContributors' => ['name' => 'MainContributors', 'is_system' => 1],
  32 + '_SystemContributors' => ['name' => 'Contributors', 'is_system' => 1],
  33 + '_SystemCustom1' => ['name' => 'Custom1', 'is_system' => 1],
  34 + '_SystemCustom2' => ['name' => 'Custom2', 'is_system' => 1],
  35 + '_SystemCustom3' => ['name' => 'Custom3', 'is_system' => 1],
  36 + '_SystemCustom4' => ['name' => 'Custom4', 'is_system' => 1],
  37 + '_SystemCustom5' => ['name' => 'Custom5', 'is_system' => 1],
  38 + ];
  39 +
  40 + if (empty($widgets)) {
  41 + $dashboardWidgets = $defaultDashboardWidgets;
  42 + } else {
  43 + foreach ($widgets as $widget) {
  44 + if ($widget['is_system']) {
  45 + $dashboardWidgets['_System' . $widget['name']] = ['name' => $widget['name'], 'is_system' => 1];
  46 + } else {
  47 + $dashboardWidgets[$widget['name']] = ['name' => $widget['name'], 'is_system' => 0];
  48 + }
  49 + }
  50 +
  51 + foreach ($defaultDashboardWidgets as $widgetName => $widget) {
  52 + $dashboardWidgets[$widgetName] = $widget;
  53 + }
  54 +
  55 +
  56 + }
  57 +
  58 + $dashboardWidgetPlugins = [];
  59 +
  60 + $hookResults = hook('admin_dashboard');
  61 +
  62 + if (!empty($hookResults)) {
  63 + foreach ($hookResults as $hookResult) {
  64 + if (isset($hookResult['width']) && isset($hookResult['view']) && isset($hookResult['plugin'])) { //验证插件返回合法性
  65 + $dashboardWidgetPlugins[$hookResult['plugin']] = $hookResult;
  66 + if (!isset($dashboardWidgets[$hookResult['plugin']])) {
  67 + $dashboardWidgets[$hookResult['plugin']] = ['name' => $hookResult['plugin'], 'is_system' => 0];
  68 + }
  69 + }
  70 + }
  71 + }
  72 +
  73 + $smtpSetting = cmf_get_option('smtp_setting');
  74 +
  75 + $this->assign('dashboard_widgets', $dashboardWidgets);
  76 + $this->assign('dashboard_widget_plugins', $dashboardWidgetPlugins);
  77 + $this->assign('has_smtp_setting', empty($smtpSetting) ? false : true);
  78 +
  79 + return $this->fetch();
  80 + }
  81 +
  82 + public function dashboardWidget()
  83 + {
  84 + $dashboardWidgets = [];
  85 + $widgets = $this->request->param('widgets/a');
  86 + if (!empty($widgets)) {
  87 + foreach ($widgets as $widget) {
  88 + if ($widget['is_system']) {
  89 + array_push($dashboardWidgets, ['name' => $widget['name'], 'is_system' => 1]);
  90 + } else {
  91 + array_push($dashboardWidgets, ['name' => $widget['name'], 'is_system' => 0]);
  92 + }
  93 + }
  94 + }
  95 +
  96 + cmf_set_option('admin_dashboard_widgets', $dashboardWidgets, true);
  97 +
  98 + $this->success('更新成功!');
  99 +
  100 + }
  101 +
  102 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use app\admin\model\AdminMenuModel;
  14 +use cmf\controller\AdminBaseController;
  15 +use think\Db;
  16 +use think\facade\Cache;
  17 +use tree\Tree;
  18 +use mindplay\annotations\Annotations;
  19 +
  20 +class MenuController extends AdminBaseController
  21 +{
  22 + /**
  23 + * 后台菜单管理
  24 + * @adminMenu(
  25 + * 'name' => '后台菜单',
  26 + * 'parent' => 'admin/Setting/default',
  27 + * 'display'=> false,
  28 + * 'hasView'=> true,
  29 + * 'order' => 10000,
  30 + * 'icon' => '',
  31 + * 'remark' => '后台菜单管理',
  32 + * 'param' => ''
  33 + * )
  34 + * @return mixed
  35 + * @throws \think\db\exception\DataNotFoundException
  36 + * @throws \think\db\exception\ModelNotFoundException
  37 + * @throws \think\exception\DbException
  38 + */
  39 + public function index()
  40 + {
  41 + $content = hook_one('admin_menu_index_view');
  42 +
  43 + if (!empty($content)) {
  44 + return $content;
  45 + }
  46 +
  47 + session('admin_menu_index', 'Menu/index');
  48 + $result = Db::name('AdminMenu')->order(["list_order" => "ASC"])->select()->toArray();
  49 + $tree = new Tree();
  50 + $tree->icon = ['&nbsp;&nbsp;&nbsp;│ ', '&nbsp;&nbsp;&nbsp;├─', '&nbsp;&nbsp;&nbsp;└─ '];
  51 + $tree->nbsp = '&nbsp;&nbsp;&nbsp;';
  52 +
  53 + $newMenus = [];
  54 + foreach ($result as $m) {
  55 + $newMenus[$m['id']] = $m;
  56 + }
  57 + foreach ($result as $key => $value) {
  58 +
  59 + $result[$key]['parent_id_node'] = ($value['parent_id']) ? ' class="child-of-node-' . $value['parent_id'] . '"' : '';
  60 + $result[$key]['style'] = empty($value['parent_id']) ? '' : 'display:none;';
  61 + $result[$key]['str_manage'] = '<a class="btn btn-xs btn-primary" href="' . url("Menu/add", ["parent_id" => $value['id'], "menu_id" => $this->request->param("menu_id")]) . '">' . lang('ADD_SUB_MENU') . '</a>
  62 + <a class="btn btn-xs btn-primary" href="' . url("Menu/edit", ["id" => $value['id'], "menu_id" => $this->request->param("menu_id")]) . '">' . lang('EDIT') . '</a>
  63 + <a class="btn btn-xs btn-danger js-ajax-delete" href="' . url("Menu/delete", ["id" => $value['id'], "menu_id" => $this->request->param("menu_id")]) . '">' . lang('DELETE') . '</a> ';
  64 + $result[$key]['status'] = $value['status'] ? '<span class="label label-success">' . lang('DISPLAY') . '</span>' : '<span class="label label-warning">' . lang('HIDDEN') . '</span>';
  65 + if (APP_DEBUG) {
  66 + $result[$key]['app'] = $value['app'] . "/" . $value['controller'] . "/" . $value['action'];
  67 + }
  68 + }
  69 +
  70 + $tree->init($result);
  71 + $str = "<tr id='node-\$id' \$parent_id_node style='\$style'>
  72 + <td style='padding-left:20px;'><input name='list_orders[\$id]' type='text' size='3' value='\$list_order' class='input input-order'></td>
  73 + <td>\$id</td>
  74 + <td>\$spacer\$name</td>
  75 + <td>\$app</td>
  76 + <td>\$status</td>
  77 + <td>\$str_manage</td>
  78 + </tr>";
  79 + $category = $tree->getTree(0, $str);
  80 + $this->assign("category", $category);
  81 + return $this->fetch();
  82 + }
  83 +
  84 + /**
  85 + * 后台所有菜单列表
  86 + * @adminMenu(
  87 + * 'name' => '所有菜单',
  88 + * 'parent' => 'index',
  89 + * 'display'=> false,
  90 + * 'hasView'=> true,
  91 + * 'order' => 10000,
  92 + * 'icon' => '',
  93 + * 'remark' => '后台所有菜单列表',
  94 + * 'param' => ''
  95 + * )
  96 + * @return mixed
  97 + * @throws \think\db\exception\DataNotFoundException
  98 + * @throws \think\db\exception\ModelNotFoundException
  99 + * @throws \think\exception\DbException
  100 + */
  101 + public function lists()
  102 + {
  103 + session('admin_menu_index', 'Menu/lists');
  104 + $result = Db::name('AdminMenu')->order(["app" => "ASC", "controller" => "ASC", "action" => "ASC"])->select();
  105 + $this->assign("menus", $result);
  106 + return $this->fetch();
  107 + }
  108 +
  109 + /**
  110 + * 后台菜单添加
  111 + * @adminMenu(
  112 + * 'name' => '后台菜单添加',
  113 + * 'parent' => 'index',
  114 + * 'display'=> false,
  115 + * 'hasView'=> true,
  116 + * 'order' => 10000,
  117 + * 'icon' => '',
  118 + * 'remark' => '后台菜单添加',
  119 + * 'param' => ''
  120 + * )
  121 + * @return mixed
  122 + * @throws \think\db\exception\DataNotFoundException
  123 + * @throws \think\db\exception\ModelNotFoundException
  124 + * @throws \think\exception\DbException
  125 + */
  126 + public function add()
  127 + {
  128 + $tree = new Tree();
  129 + $parentId = $this->request->param("parent_id", 0, 'intval');
  130 + $result = Db::name('AdminMenu')->order(["list_order" => "ASC"])->select();
  131 + $array = [];
  132 + foreach ($result as $r) {
  133 + $r['selected'] = $r['id'] == $parentId ? 'selected' : '';
  134 + $array[] = $r;
  135 + }
  136 + $str = "<option value='\$id' \$selected>\$spacer \$name</option>";
  137 + $tree->init($array);
  138 + $selectCategory = $tree->getTree(0, $str);
  139 + $this->assign("select_category", $selectCategory);
  140 + return $this->fetch();
  141 + }
  142 +
  143 + /**
  144 + * 后台菜单添加提交保存
  145 + * @adminMenu(
  146 + * 'name' => '后台菜单添加提交保存',
  147 + * 'parent' => 'index',
  148 + * 'display'=> false,
  149 + * 'hasView'=> false,
  150 + * 'order' => 10000,
  151 + * 'icon' => '',
  152 + * 'remark' => '后台菜单添加提交保存',
  153 + * 'param' => ''
  154 + * )
  155 + */
  156 + public function addPost()
  157 + {
  158 + if ($this->request->isPost()) {
  159 + $result = $this->validate($this->request->param(), 'AdminMenu');
  160 + if ($result !== true) {
  161 + $this->error($result);
  162 + } else {
  163 + $data = $this->request->param();
  164 + Db::name('AdminMenu')->strict(false)->field(true)->insert($data);
  165 +
  166 + $app = $this->request->param("app");
  167 + $controller = $this->request->param("controller");
  168 + $action = $this->request->param("action");
  169 + $param = $this->request->param("param");
  170 + $authRuleName = "$app/$controller/$action";
  171 + $menuName = $this->request->param("name");
  172 +
  173 + $findAuthRuleCount = Db::name('auth_rule')->where([
  174 + 'app' => $app,
  175 + 'name' => $authRuleName,
  176 + 'type' => 'admin_url'
  177 + ])->count();
  178 + if (empty($findAuthRuleCount)) {
  179 + Db::name('AuthRule')->insert([
  180 + "name" => $authRuleName,
  181 + "app" => $app,
  182 + "type" => "admin_url", //type 1-admin rule;2-user rule
  183 + "title" => $menuName,
  184 + 'param' => $param,
  185 + ]);
  186 + }
  187 + $sessionAdminMenuIndex = session('admin_menu_index');
  188 + $to = empty($sessionAdminMenuIndex) ? "Menu/index" : $sessionAdminMenuIndex;
  189 + $this->_exportAppMenuDefaultLang();
  190 + Cache::clear('admin_menus');// 删除后台菜单缓存
  191 + $this->success("添加成功!", url($to));
  192 + }
  193 + }
  194 + }
  195 +
  196 + /**
  197 + * 后台菜单编辑
  198 + * @adminMenu(
  199 + * 'name' => '后台菜单编辑',
  200 + * 'parent' => 'index',
  201 + * 'display'=> false,
  202 + * 'hasView'=> true,
  203 + * 'order' => 10000,
  204 + * 'icon' => '',
  205 + * 'remark' => '后台菜单编辑',
  206 + * 'param' => ''
  207 + * )
  208 + * @return mixed
  209 + * @throws \think\db\exception\DataNotFoundException
  210 + * @throws \think\db\exception\ModelNotFoundException
  211 + * @throws \think\exception\DbException
  212 + */
  213 + public function edit()
  214 + {
  215 + $tree = new Tree();
  216 + $id = $this->request->param("id", 0, 'intval');
  217 + $rs = Db::name('AdminMenu')->where("id", $id)->find();
  218 + $result = Db::name('AdminMenu')->order(["list_order" => "ASC"])->select();
  219 + $array = [];
  220 + foreach ($result as $r) {
  221 + $r['selected'] = $r['id'] == $rs['parent_id'] ? 'selected' : '';
  222 + $array[] = $r;
  223 + }
  224 + $str = "<option value='\$id' \$selected>\$spacer \$name</option>";
  225 + $tree->init($array);
  226 + $selectCategory = $tree->getTree(0, $str);
  227 + $this->assign("data", $rs);
  228 + $this->assign("select_category", $selectCategory);
  229 + return $this->fetch();
  230 + }
  231 +
  232 + /**
  233 + * 后台菜单编辑提交保存
  234 + * @adminMenu(
  235 + * 'name' => '后台菜单编辑提交保存',
  236 + * 'parent' => 'index',
  237 + * 'display'=> false,
  238 + * 'hasView'=> false,
  239 + * 'order' => 10000,
  240 + * 'icon' => '',
  241 + * 'remark' => '后台菜单编辑提交保存',
  242 + * 'param' => ''
  243 + * )
  244 + * @throws \think\Exception
  245 + * @throws \think\db\exception\DataNotFoundException
  246 + * @throws \think\db\exception\ModelNotFoundException
  247 + * @throws \think\exception\DbException
  248 + * @throws \think\exception\PDOException
  249 + */
  250 + public function editPost()
  251 + {
  252 + if ($this->request->isPost()) {
  253 + $id = $this->request->param('id', 0, 'intval');
  254 + $oldMenu = Db::name('AdminMenu')->where('id', $id)->find();
  255 +
  256 + $result = $this->validate($this->request->param(), 'AdminMenu.edit');
  257 +
  258 + if ($result !== true) {
  259 + $this->error($result);
  260 + } else {
  261 + Db::name('AdminMenu')->strict(false)->field(true)->update($this->request->param());
  262 + $app = $this->request->param("app");
  263 + $controller = $this->request->param("controller");
  264 + $action = $this->request->param("action");
  265 + $param = $this->request->param("param");
  266 + $authRuleName = "$app/$controller/$action";
  267 + $menuName = $this->request->param("name");
  268 +
  269 + $findAuthRuleCount = Db::name('auth_rule')->where([
  270 + 'app' => $app,
  271 + 'name' => $authRuleName,
  272 + 'type' => 'admin_url'
  273 + ])->count();
  274 + if (empty($findAuthRuleCount)) {
  275 + $oldApp = $oldMenu['app'];
  276 + $oldController = $oldMenu['controller'];
  277 + $oldAction = $oldMenu['action'];
  278 + $oldName = "$oldApp/$oldController/$oldAction";
  279 + $findOldRuleId = Db::name('AuthRule')->where("name", $oldName)->value('id');
  280 + if (empty($findOldRuleId)) {
  281 + Db::name('AuthRule')->insert([
  282 + "name" => $authRuleName,
  283 + "app" => $app,
  284 + "type" => "admin_url",
  285 + "title" => $menuName,
  286 + "param" => $param
  287 + ]);//type 1-admin rule;2-user rule
  288 + } else {
  289 + Db::name('AuthRule')->where('id', $findOldRuleId)->update([
  290 + "name" => $authRuleName,
  291 + "app" => $app,
  292 + "type" => "admin_url",
  293 + "title" => $menuName,
  294 + "param" => $param]);//type 1-admin rule;2-user rule
  295 + }
  296 + } else {
  297 + Db::name('AuthRule')->where([
  298 + 'app' => $app,
  299 + 'name' => $authRuleName,
  300 + 'type' => 'admin_url'
  301 + ])->update(["title" => $menuName, 'param' => $param]);//type 1-admin rule;2-user rule
  302 + }
  303 + $this->_exportAppMenuDefaultLang();
  304 + Cache::clear('admin_menus');// 删除后台菜单缓存
  305 + $this->success("保存成功!");
  306 + }
  307 + }
  308 + }
  309 +
  310 + /**
  311 + * 后台菜单删除
  312 + * @adminMenu(
  313 + * 'name' => '后台菜单删除',
  314 + * 'parent' => 'index',
  315 + * 'display'=> false,
  316 + * 'hasView'=> false,
  317 + * 'order' => 10000,
  318 + * 'icon' => '',
  319 + * 'remark' => '后台菜单删除',
  320 + * 'param' => ''
  321 + * )
  322 + * @throws \think\Exception
  323 + * @throws \think\exception\PDOException
  324 + */
  325 + public function delete()
  326 + {
  327 + $id = $this->request->param("id", 0, 'intval');
  328 + $count = Db::name('AdminMenu')->where("parent_id", $id)->count();
  329 + if ($count > 0) {
  330 + $this->error("该菜单下还有子菜单,无法删除!");
  331 + }
  332 + if (Db::name('AdminMenu')->delete($id) !== false) {
  333 + $this->success("删除菜单成功!");
  334 + } else {
  335 + $this->error("删除失败!");
  336 + }
  337 + }
  338 +
  339 + /**
  340 + * 后台菜单排序
  341 + * @adminMenu(
  342 + * 'name' => '后台菜单排序',
  343 + * 'parent' => 'index',
  344 + * 'display'=> false,
  345 + * 'hasView'=> false,
  346 + * 'order' => 10000,
  347 + * 'icon' => '',
  348 + * 'remark' => '后台菜单排序',
  349 + * 'param' => ''
  350 + * )
  351 + */
  352 + public function listOrder()
  353 + {
  354 + $adminMenuModel = new AdminMenuModel();
  355 + parent::listOrders($adminMenuModel);
  356 + $this->success("排序更新成功!");
  357 + }
  358 +
  359 + /**
  360 + * 导入新后台菜单
  361 + * @adminMenu(
  362 + * 'name' => '导入新后台菜单',
  363 + * 'parent' => 'index',
  364 + * 'display'=> false,
  365 + * 'hasView'=> true,
  366 + * 'order' => 10000,
  367 + * 'icon' => '',
  368 + * 'remark' => '导入新后台菜单',
  369 + * 'param' => ''
  370 + * )
  371 + * @return mixed
  372 + * @throws \ReflectionException
  373 + * @throws \think\Exception
  374 + * @throws \think\db\exception\DataNotFoundException
  375 + * @throws \think\db\exception\ModelNotFoundException
  376 + * @throws \think\exception\DbException
  377 + * @throws \think\exception\PDOException
  378 + */
  379 + public function getActions()
  380 + {
  381 + Annotations::$config['cache'] = false;
  382 + $annotationManager = Annotations::getManager();
  383 + $annotationManager->registry['adminMenu'] = 'app\admin\annotation\AdminMenuAnnotation';
  384 + $annotationManager->registry['adminMenuRoot'] = 'app\admin\annotation\AdminMenuRootAnnotation';
  385 + $newMenus = [];
  386 +
  387 + $apps = cmf_scan_dir(APP_PATH . '*', GLOB_ONLYDIR);
  388 +
  389 + $app = $this->request->param('app', '');
  390 + if (empty($app)) {
  391 + $app = $apps[0];
  392 + }
  393 +
  394 + if (!in_array($app, $apps)) {
  395 + $this->error('应用' . $app . '不存在!');
  396 + }
  397 +
  398 + if ($app == 'admin') {
  399 + $filePatten = APP_PATH . $app . '/controller/*Controller.php';
  400 + } else {
  401 + $filePatten = APP_PATH . $app . '/controller/Admin*Controller.php';
  402 + }
  403 +
  404 + $controllers = cmf_scan_dir($filePatten);
  405 +
  406 + if (!empty($controllers)) {
  407 + foreach ($controllers as $controller) {
  408 + $controller = preg_replace('/\.php$/', '', $controller);
  409 + $controllerName = preg_replace('/\Controller$/', '', $controller);
  410 + $controllerClass = "app\\$app\\controller\\$controller";
  411 +
  412 + $menuAnnotations = Annotations::ofClass($controllerClass, '@adminMenuRoot');
  413 +
  414 + if (!empty($menuAnnotations)) {
  415 + foreach ($menuAnnotations as $menuAnnotation) {
  416 +
  417 + $name = $menuAnnotation->name;
  418 + $icon = $menuAnnotation->icon;
  419 + $type = 0;//1:有界面可访问菜单,2:无界面可访问菜单,0:只作为菜单
  420 + $action = $menuAnnotation->action;
  421 + $status = empty($menuAnnotation->display) ? 0 : 1;
  422 + $listOrder = floatval($menuAnnotation->order);
  423 + $param = $menuAnnotation->param;
  424 + $remark = $menuAnnotation->remark;
  425 +
  426 + if (empty($menuAnnotation->parent)) {
  427 + $parentId = 0;
  428 + } else {
  429 +
  430 + $parent = explode('/', $menuAnnotation->parent);
  431 + $countParent = count($parent);
  432 + if ($countParent > 3) {
  433 + throw new \Exception($controllerClass . ':' . $action . ' @adminMenuRoot parent格式不正确!');
  434 + }
  435 +
  436 + $parentApp = $app;
  437 + $parentController = $controllerName;
  438 + $parentAction = '';
  439 +
  440 + switch ($countParent) {
  441 + case 1:
  442 + $parentAction = $parent[0];
  443 + break;
  444 + case 2:
  445 + $parentController = $parent[0];
  446 + $parentAction = $parent[1];
  447 + break;
  448 + case 3:
  449 + $parentApp = $parent[0];
  450 + $parentController = $parent[1];
  451 + $parentAction = $parent[2];
  452 + break;
  453 + }
  454 +
  455 + $findParentAdminMenu = Db::name('admin_menu')->where([
  456 + 'app' => $parentApp,
  457 + 'controller' => $parentController,
  458 + 'action' => $parentAction
  459 + ])->find();
  460 +
  461 + if (empty($findParentAdminMenu)) {
  462 + $parentId = Db::name('admin_menu')->insertGetId([
  463 + 'app' => $parentApp,
  464 + 'controller' => $parentController,
  465 + 'action' => $parentAction,
  466 + 'name' => '--new--'
  467 + ]);
  468 + } else {
  469 + $parentId = $findParentAdminMenu['id'];
  470 + }
  471 + }
  472 +
  473 + $findAdminMenu = Db::name('admin_menu')->where([
  474 + 'app' => $app,
  475 + 'controller' => $controllerName,
  476 + 'action' => $action
  477 + ])->find();
  478 +
  479 + if (empty($findAdminMenu)) {
  480 +
  481 + Db::name('admin_menu')->insert([
  482 + 'parent_id' => $parentId,
  483 + 'type' => $type,
  484 + 'status' => $status,
  485 + 'list_order' => $listOrder,
  486 + 'app' => $app,
  487 + 'controller' => $controllerName,
  488 + 'action' => $action,
  489 + 'param' => $param,
  490 + 'name' => $name,
  491 + 'icon' => $icon,
  492 + 'remark' => $remark
  493 + ]);
  494 +
  495 + $menuName = $name;
  496 +
  497 + array_push($newMenus, "$app/$controllerName/$action 已导入");
  498 +
  499 + } else {
  500 +
  501 + if ($findAdminMenu['name'] == '--new--') {
  502 + Db::name('admin_menu')->where([
  503 + 'app' => $app,
  504 + 'controller' => $controllerName,
  505 + 'action' => $action
  506 + ])->update([
  507 + 'parent_id' => $parentId,
  508 + 'type' => $type,
  509 + 'status' => $status,
  510 + 'list_order' => $listOrder,
  511 + 'param' => $param,
  512 + 'name' => $name,
  513 + 'icon' => $icon,
  514 + 'remark' => $remark
  515 + ]);
  516 + $menuName = $name;
  517 + } else {
  518 + // 只关注菜单层级关系,是否有视图
  519 + Db::name('admin_menu')->where([
  520 + 'app' => $app,
  521 + 'controller' => $controllerName,
  522 + 'action' => $action
  523 + ])->update([
  524 + //'parent_id' => $parentId,
  525 + 'type' => $type,
  526 + ]);
  527 + $menuName = $findAdminMenu['name'];
  528 + }
  529 +
  530 + array_push($newMenus, "$app/$controllerName/$action 层级关系已更新");
  531 + }
  532 +
  533 + $authRuleName = "{$app}/{$controllerName}/{$action}";
  534 + $findAuthRuleCount = Db::name('auth_rule')->where([
  535 + 'app' => $app,
  536 + 'name' => $authRuleName,
  537 + 'type' => 'admin_url'
  538 + ])->count();
  539 +
  540 + if ($findAuthRuleCount == 0) {
  541 + Db::name('auth_rule')->insert([
  542 + 'app' => $app,
  543 + 'name' => $authRuleName,
  544 + 'type' => 'admin_url',
  545 + 'param' => $param,
  546 + 'title' => $menuName
  547 + ]);
  548 + } else {
  549 + Db::name('auth_rule')->where([
  550 + 'app' => $app,
  551 + 'name' => $authRuleName,
  552 + 'type' => 'admin_url',
  553 + ])->update([
  554 + 'param' => $param,
  555 + 'title' => $menuName
  556 + ]);
  557 + }
  558 +
  559 + }
  560 + }
  561 +
  562 + $reflect = new \ReflectionClass($controllerClass);
  563 + $methods = $reflect->getMethods(\ReflectionMethod::IS_PUBLIC);
  564 +
  565 + if (!empty($methods)) {
  566 + foreach ($methods as $method) {
  567 +
  568 + if ($method->class == $controllerClass && strpos($method->name, '_') !== 0) {
  569 + $menuAnnotations = Annotations::ofMethod($controllerClass, $method->name, '@adminMenu');
  570 +
  571 + if (!empty($menuAnnotations)) {
  572 +
  573 + $menuAnnotation = $menuAnnotations[0];
  574 +
  575 + $name = $menuAnnotation->name;
  576 + $icon = $menuAnnotation->icon;
  577 + $type = $menuAnnotation->hasView ? 1 : 2;//1:有界面可访问菜单,2:无界面可访问菜单,0:只作为菜单
  578 + $action = $method->name;
  579 + $status = empty($menuAnnotation->display) ? 0 : 1;
  580 + $listOrder = floatval($menuAnnotation->order);
  581 + $param = $menuAnnotation->param;
  582 + $remark = $menuAnnotation->remark;
  583 +
  584 + if (empty($menuAnnotation->parent)) {
  585 + $parentId = 0;
  586 + } else {
  587 + $parent = explode('/', $menuAnnotation->parent);
  588 + $countParent = count($parent);
  589 + if ($countParent > 3) {
  590 + throw new \Exception($controllerClass . ':' . $action . ' @menuRoot parent格式不正确!');
  591 + }
  592 +
  593 + $parentApp = $app;
  594 + $parentController = $controllerName;
  595 + $parentAction = '';
  596 +
  597 + switch ($countParent) {
  598 + case 1:
  599 + $parentAction = $parent[0];
  600 + break;
  601 + case 2:
  602 + $parentController = $parent[0];
  603 + $parentAction = $parent[1];
  604 + break;
  605 + case 3:
  606 + $parentApp = $parent[0];
  607 + $parentController = $parent[1];
  608 + $parentAction = $parent[2];
  609 + break;
  610 + }
  611 +
  612 + $findParentAdminMenu = Db::name('admin_menu')->where([
  613 + 'app' => $parentApp,
  614 + 'controller' => $parentController,
  615 + 'action' => $parentAction
  616 + ])->find();
  617 +
  618 + if (empty($findParentAdminMenu)) {
  619 + $parentId = Db::name('admin_menu')->insertGetId([
  620 + 'app' => $parentApp,
  621 + 'controller' => $parentController,
  622 + 'action' => $parentAction,
  623 + 'name' => '--new--'
  624 + ]);
  625 + } else {
  626 + $parentId = $findParentAdminMenu['id'];
  627 + }
  628 + }
  629 +
  630 + $findAdminMenu = Db::name('admin_menu')->where([
  631 + 'app' => $app,
  632 + 'controller' => $controllerName,
  633 + 'action' => $action
  634 + ])->find();
  635 +
  636 + if (empty($findAdminMenu)) {
  637 +
  638 + Db::name('admin_menu')->insert([
  639 + 'parent_id' => $parentId,
  640 + 'type' => $type,
  641 + 'status' => $status,
  642 + 'list_order' => $listOrder,
  643 + 'app' => $app,
  644 + 'controller' => $controllerName,
  645 + 'action' => $action,
  646 + 'param' => $param,
  647 + 'name' => $name,
  648 + 'icon' => $icon,
  649 + 'remark' => $remark
  650 + ]);
  651 +
  652 + $menuName = $name;
  653 +
  654 + array_push($newMenus, "$app/$controllerName/$action 已导入");
  655 +
  656 + } else {
  657 + if ($findAdminMenu['name'] == '--new--') {
  658 + Db::name('admin_menu')->where([
  659 + 'app' => $app,
  660 + 'controller' => $controllerName,
  661 + 'action' => $action
  662 + ])->update([
  663 + 'parent_id' => $parentId,
  664 + 'type' => $type,
  665 + 'status' => $status,
  666 + 'list_order' => $listOrder,
  667 + 'param' => $param,
  668 + 'name' => $name,
  669 + 'icon' => $icon,
  670 + 'remark' => $remark
  671 + ]);
  672 + $menuName = $name;
  673 + } else {
  674 + // 只关注菜单层级关系,是否有视图
  675 + Db::name('admin_menu')->where([
  676 + 'app' => $app,
  677 + 'controller' => $controllerName,
  678 + 'action' => $action
  679 + ])->update([
  680 + //'parent_id' => $parentId,
  681 + 'type' => $type,
  682 + ]);
  683 + $menuName = $findAdminMenu['name'];
  684 + }
  685 +
  686 +
  687 + array_push($newMenus, "$app/$controllerName/$action 已更新");
  688 + }
  689 +
  690 + $authRuleName = "{$app}/{$controllerName}/{$action}";
  691 + $findAuthRuleCount = Db::name('auth_rule')->where([
  692 + 'app' => $app,
  693 + 'name' => $authRuleName,
  694 + 'type' => 'admin_url'
  695 + ])->count();
  696 +
  697 + if ($findAuthRuleCount == 0) {
  698 + Db::name('auth_rule')->insert([
  699 + 'app' => $app,
  700 + 'name' => $authRuleName,
  701 + 'type' => 'admin_url',
  702 + 'param' => $param,
  703 + 'title' => $menuName
  704 + ]);
  705 + } else {
  706 + Db::name('auth_rule')->where([
  707 + 'app' => $app,
  708 + 'name' => $authRuleName,
  709 + 'type' => 'admin_url',
  710 + ])->update([
  711 + 'param' => $param,
  712 + 'title' => $menuName
  713 + ]);
  714 + }
  715 + }
  716 +
  717 + }
  718 + }
  719 + }
  720 +
  721 + }
  722 + }
  723 +
  724 + $index = array_search($app, $apps);
  725 + $nextIndex = $index + 1;
  726 + $nextIndex = $nextIndex >= count($apps) ? 0 : $nextIndex;
  727 + if ($nextIndex) {
  728 + $this->assign("next_app", $apps[$nextIndex]);
  729 + }
  730 + $this->assign("app", $app);
  731 + $this->assign("new_menus", $newMenus);
  732 +
  733 + Cache::clear('admin_menus');// 删除后台菜单缓存
  734 +
  735 + return $this->fetch();
  736 +
  737 + }
  738 +
  739 + /**
  740 + * 导出后台菜单语言包
  741 + * @throws \think\db\exception\DataNotFoundException
  742 + * @throws \think\db\exception\ModelNotFoundException
  743 + * @throws \think\exception\DbException
  744 + */
  745 + private function _exportAppMenuDefaultLang()
  746 + {
  747 + $menus = Db::name('AdminMenu')->order(["app" => "ASC", "controller" => "ASC", "action" => "ASC"])->select();
  748 + $langDir = config('DEFAULT_LANG');
  749 + $adminMenuLang = CMF_ROOT . "data/lang/" . $langDir . "/admin_menu.php";
  750 +
  751 + if (!empty($adminMenuLang) && !file_exists_case($adminMenuLang)) {
  752 + mkdir(dirname($adminMenuLang), 0777, true);
  753 + }
  754 +
  755 + $lang = [];
  756 +
  757 + foreach ($menus as $menu) {
  758 + $lang_key = strtoupper($menu['app'] . '_' . $menu['controller'] . '_' . $menu['action']);
  759 + $lang[$lang_key] = $menu['name'];
  760 + }
  761 +
  762 + $langStr = var_export($lang, true);
  763 + $langStr = preg_replace("/\s+\d+\s=>\s(\n|\r)/", "\n", $langStr);
  764 +
  765 + if (!empty($adminMenuLang)) {
  766 + file_put_contents($adminMenuLang, "<?php\nreturn $langStr;");
  767 + }
  768 + }
  769 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: kane <chengjin005@163.com> 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\NavModel;
  15 +use think\Db;
  16 +
  17 +/**
  18 + * Class NavController 导航类别管理控制器
  19 + * @package app\admin\controller
  20 + */
  21 +class NavController extends AdminBaseController
  22 +{
  23 + /**
  24 + * 导航管理
  25 + * @adminMenu(
  26 + * 'name' => '导航管理',
  27 + * 'parent' => 'admin/Setting/default',
  28 + * 'display'=> true,
  29 + * 'hasView'=> true,
  30 + * 'order' => 30,
  31 + * 'icon' => '',
  32 + * 'remark' => '导航管理',
  33 + * 'param' => ''
  34 + * )
  35 + */
  36 + public function index()
  37 + {
  38 + $content = hook_one('admin_nav_index_view');
  39 +
  40 + if (!empty($content)) {
  41 + return $content;
  42 + }
  43 +
  44 + $navModel = new NavModel();
  45 +
  46 + $navs = $navModel->select();
  47 + $this->assign('navs', $navs);
  48 +
  49 + return $this->fetch();
  50 +
  51 + }
  52 +
  53 + /**
  54 + * 添加导航
  55 + * @adminMenu(
  56 + * 'name' => '添加导航',
  57 + * 'parent' => 'index',
  58 + * 'display'=> false,
  59 + * 'hasView'=> true,
  60 + * 'order' => 10000,
  61 + * 'icon' => '',
  62 + * 'remark' => '添加导航',
  63 + * 'param' => ''
  64 + * )
  65 + */
  66 + public function add()
  67 + {
  68 + return $this->fetch();
  69 + }
  70 +
  71 + /**
  72 + * 添加导航提交保存
  73 + * @adminMenu(
  74 + * 'name' => '添加导航提交保存',
  75 + * 'parent' => 'index',
  76 + * 'display'=> false,
  77 + * 'hasView'=> false,
  78 + * 'order' => 10000,
  79 + * 'icon' => '',
  80 + * 'remark' => '添加导航提交保存',
  81 + * 'param' => ''
  82 + * )
  83 + */
  84 + public function addPost()
  85 + {
  86 +
  87 + $navModel = new NavModel();
  88 + $arrData = $this->request->post();
  89 +
  90 + if (empty($arrData["is_main"])) {
  91 + $arrData["is_main"] = 0;
  92 + } else {
  93 + $navModel->where("is_main", 1)->update(["is_main" => 0]);
  94 + }
  95 +
  96 + $navModel->allowField(true)->insert($arrData);
  97 + $this->success(lang("EDIT_SUCCESS"), url("nav/index"));
  98 +
  99 + }
  100 +
  101 + /**
  102 + * 编辑导航
  103 + * @adminMenu(
  104 + * 'name' => '编辑导航',
  105 + * 'parent' => 'index',
  106 + * 'display'=> false,
  107 + * 'hasView'=> true,
  108 + * 'order' => 10000,
  109 + * 'icon' => '',
  110 + * 'remark' => '编辑导航',
  111 + * 'param' => ''
  112 + * )
  113 + */
  114 + public function edit()
  115 + {
  116 + $navModel = new NavModel();
  117 + $intId = $this->request->param("id", 0, 'intval');
  118 +
  119 + $objNavCat = $navModel->where("id", $intId)->find();
  120 + $arrNavCat = $objNavCat ? $objNavCat->toArray() : [];
  121 +
  122 + $this->assign($arrNavCat);
  123 + return $this->fetch();
  124 + }
  125 +
  126 +
  127 + /**
  128 + * 编辑导航提交保存
  129 + * @adminMenu(
  130 + * 'name' => '编辑导航提交保存',
  131 + * 'parent' => 'index',
  132 + * 'display'=> false,
  133 + * 'hasView'=> false,
  134 + * 'order' => 10000,
  135 + * 'icon' => '',
  136 + * 'remark' => '编辑导航提交保存',
  137 + * 'param' => ''
  138 + * )
  139 + */
  140 + public function editPost()
  141 + {
  142 +
  143 + $navModel = new NavModel();
  144 + $arrData = $this->request->post();
  145 +
  146 + if (empty($arrData["is_main"])) {
  147 + $arrData["is_main"] = 0;
  148 + } else {
  149 + $navModel->where("is_main", 1)->update(["is_main" => 0]);
  150 + }
  151 +
  152 + $navModel->allowField(true)->where("id", intval($arrData["id"]))->update($arrData);
  153 + $this->success(lang("EDIT_SUCCESS"), url("nav/index"));
  154 +
  155 + }
  156 +
  157 + /**
  158 + * 删除导航
  159 + * @adminMenu(
  160 + * 'name' => '删除导航',
  161 + * 'parent' => 'index',
  162 + * 'display'=> false,
  163 + * 'hasView'=> false,
  164 + * 'order' => 10000,
  165 + * 'icon' => '',
  166 + * 'remark' => '删除导航',
  167 + * 'param' => ''
  168 + * )
  169 + */
  170 + public function delete()
  171 + {
  172 + $navModel = new NavModel();
  173 + $intId = $this->request->param("id", 0, "intval");
  174 +
  175 + if (empty($intId)) {
  176 + $this->error(lang("NO_ID"));
  177 + }
  178 +
  179 + $navModel->where("id", $intId)->delete();
  180 + $this->success(lang("DELETE_SUCCESS"), url("nav/index"));
  181 +
  182 + }
  183 +
  184 +
  185 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: kane <chengjin005@163.com> 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use app\admin\model\NavMenuModel;
  14 +use cmf\controller\AdminBaseController;
  15 +use tree\Tree;
  16 +
  17 +/**
  18 + * Class NavMenuController 前台菜单管理控制器
  19 + * @package app\admin\controller
  20 + */
  21 +class NavMenuController extends AdminBaseController
  22 +{
  23 + /**
  24 + * 导航菜单
  25 + * @adminMenu(
  26 + * 'name' => '导航菜单',
  27 + * 'parent' => 'admin/Nav/index',
  28 + * 'display'=> false,
  29 + * 'hasView'=> true,
  30 + * 'order' => 10000,
  31 + * 'icon' => '',
  32 + * 'remark' => '导航菜单',
  33 + * 'param' => ''
  34 + * )
  35 + */
  36 + public function index()
  37 + {
  38 + $intNavId = $this->request->param("nav_id", 0, 'intval');
  39 + $navMenuModel = new NavMenuModel();
  40 +
  41 + if (empty($intNavId)) {
  42 + $this->error("请指定导航!");
  43 + }
  44 +
  45 + $objResult = $navMenuModel->where("nav_id", $intNavId)->order(["list_order" => "ASC"])->select();
  46 + $arrResult = $objResult ? $objResult->toArray() : [];
  47 +
  48 + $tree = new Tree();
  49 + $tree->icon = ['&nbsp;&nbsp;&nbsp;│ ', '&nbsp;&nbsp;&nbsp;├─ ', '&nbsp;&nbsp;&nbsp;└─ '];
  50 + $tree->nbsp = '&nbsp;&nbsp;&nbsp;';
  51 +
  52 + $array = [];
  53 + foreach ($arrResult as $r) {
  54 + $r['str_manage'] = '<a class="btn btn-xs btn-primary" href="' . url("NavMenu/add", ["parent_id" => $r['id'], "nav_id" => $r['nav_id']]) . '">添加子菜单</a>
  55 + <a class="btn btn-xs btn-primary" href="' . url("NavMenu/edit", ["id" => $r['id'], "parent_id" => $r['parent_id'], "nav_id" => $r['nav_id']]) . '">编辑</a>
  56 + <a class="btn btn-xs btn-danger js-ajax-delete" href="' . url("NavMenu/delete", ["id" => $r['id'], 'nav_id' => $r['nav_id']]) . '">删除</a> ';
  57 + $r['status'] = $r['status'] ? "显示" : "隐藏";
  58 + $array[] = $r;
  59 + }
  60 +
  61 + $tree->init($array);
  62 + $str = "<tr>
  63 + <td><input name='list_orders[\$id]' type='text' size='3' value='\$list_order' class='input input-order'></td>
  64 + <td>\$id</td>
  65 + <td >\$spacer\$name</td>
  66 + <td>\$status</td>
  67 + <td>\$str_manage</td>
  68 + </tr>";
  69 +
  70 + $categories = $tree->getTree(0, $str);
  71 +
  72 + $this->assign("categories", $categories);
  73 + $this->assign('nav_id', $intNavId);
  74 +
  75 + return $this->fetch();
  76 + }
  77 +
  78 + /**
  79 + * 添加导航菜单
  80 + * @adminMenu(
  81 + * 'name' => '添加导航菜单',
  82 + * 'parent' => 'index',
  83 + * 'display'=> false,
  84 + * 'order' => 10000,
  85 + * 'hasView'=> true,
  86 + * 'icon' => '',
  87 + * 'remark' => '添加导航菜单',
  88 + * 'param' => ''
  89 + * )
  90 + */
  91 + public function add()
  92 + {
  93 + $navMenuModel = new NavMenuModel();
  94 + $intNavId = $this->request->param("nav_id", 0, 'intval');
  95 + $intParentId = $this->request->param("parent_id", 0, 'intval');
  96 + $objResult = $navMenuModel->where("nav_id", $intNavId)->order(["list_order" => "ASC"])->select();
  97 + $arrResult = $objResult ? $objResult->toArray() : [];
  98 +
  99 + $tree = new Tree();
  100 + $tree->icon = ['&nbsp;│ ', '&nbsp;├─ ', '&nbsp;└─ '];
  101 + $tree->nbsp = '&nbsp;';
  102 + $array = [];
  103 +
  104 + foreach ($arrResult as $r) {
  105 + $r['str_manage'] = '<a href="' . url("NavMenu/add", ["parent_id" => $r['id']]) . '">添加子菜单</a> | <a href="'
  106 + . url("NavMenu/edit", ["id" => $r['id']]) . '">编辑</a> | <a class="J_ajax_del" href="'
  107 + . url("NavMenu/delete", ["id" => $r['id']]) . '">删除</a> ';
  108 + $r['status'] = $r['status'] ? "显示" : "隐藏";
  109 + $r['selected'] = $r['id'] == $intParentId ? "selected" : "";
  110 + $array[] = $r;
  111 + }
  112 +
  113 + $tree->init($array);
  114 + $str = "<option value='\$id' \$selected>\$spacer\$name</option>";
  115 + $navTrees = $tree->getTree(0, $str);
  116 + $this->assign("nav_trees", $navTrees);
  117 +
  118 + $navs = $navMenuModel->selectNavs();
  119 + $this->assign('navs', $navs);
  120 +
  121 + $this->assign("nav_id", $intNavId);
  122 + return $this->fetch();
  123 + }
  124 +
  125 + /**
  126 + * 添加导航菜单提交保存
  127 + * @adminMenu(
  128 + * 'name' => '添加导航菜单提交保存',
  129 + * 'parent' => 'index',
  130 + * 'display'=> false,
  131 + * 'hasView'=> false,
  132 + * 'order' => 10000,
  133 + * 'icon' => '',
  134 + * 'remark' => '添加导航菜单提交保存',
  135 + * 'param' => ''
  136 + * )
  137 + */
  138 + public function addPost()
  139 + {
  140 + $navMenuModel = new NavMenuModel();
  141 + $arrData = $this->request->post();
  142 +
  143 + if (isset($arrData['external_href'])) {
  144 + $arrData['href'] = htmlspecialchars_decode($arrData['external_href']);
  145 + } else {
  146 + $arrData['href'] = htmlspecialchars_decode($arrData['href']);
  147 + $arrData['href'] = base64_decode($arrData['href']);
  148 + }
  149 +
  150 + $navMenuModel->allowField(true)->isUpdate(false)->save($arrData);
  151 +
  152 + $this->success(lang("EDIT_SUCCESS"), url("NavMenu/index", ['nav_id' => $arrData['nav_id']]));
  153 +
  154 + }
  155 +
  156 + /**
  157 + * 编辑导航菜单
  158 + * @adminMenu(
  159 + * 'name' => '编辑导航菜单',
  160 + * 'parent' => 'index',
  161 + * 'display'=> false,
  162 + * 'hasView'=> true,
  163 + * 'order' => 10000,
  164 + * 'icon' => '',
  165 + * 'remark' => '编辑导航菜单',
  166 + * 'param' => ''
  167 + * )
  168 + */
  169 + public function edit()
  170 + {
  171 + $navMenuModel = new NavMenuModel();
  172 + $intNavId = $this->request->param("nav_id", 0, 'intval');
  173 + $intId = $this->request->param("id", 0, 'intval');
  174 + $intParentId = $this->request->param("parent_id", 0, 'intval');
  175 + $objResult = $navMenuModel
  176 + ->where("nav_id", $intNavId)
  177 + ->where("id", "neq", $intId)
  178 + ->order(["list_order" => "ASC"])
  179 + ->select();
  180 + $arrResult = $objResult ? $objResult->toArray() : [];
  181 +
  182 + $tree = new Tree();
  183 + $tree->icon = ['&nbsp;│ ', '&nbsp;├─ ', '&nbsp;└─ '];
  184 + $tree->nbsp = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
  185 + $array = [];
  186 + foreach ($arrResult as $r) {
  187 + $r['str_manage'] = '<a href="' . url("NavMenu/add", ["parent_id" => $r['id'], "nav_id" => $intNavId]) . '">添加子菜单</a> | <a href="'
  188 + . url("NavMenu/edit", ["id" => $r['id'], "nav_id" => $intNavId]) . '">编辑</a> | <a class="js-ajax-delete" href="'
  189 + . url("NavMenu/delete", ["id" => $r['id'], "nav_id" => $intNavId]) . '">删除</a> ';
  190 + $r['status'] = $r['status'] ? "显示" : "隐藏";
  191 + $r['selected'] = $r['id'] == $intParentId ? "selected" : "";
  192 + $array[] = $r;
  193 + }
  194 +
  195 + $tree->init($array);
  196 + $str = "<option value='\$id' \$selected>\$spacer\$name</option>";
  197 + $nav_trees = $tree->getTree(0, $str);
  198 + $this->assign("nav_trees", $nav_trees);
  199 +
  200 + $objNav = $navMenuModel->where("id", $intId)->find();
  201 + $arrNav = $objNav ? $objNav->toArray() : [];
  202 +
  203 + $arrNav['href_old'] = $arrNav['href'];
  204 +
  205 + if (strpos($arrNav['href'], "{") === 0 || $arrNav['href'] == 'home') {
  206 + $arrNav['href'] = base64_encode($arrNav['href']);
  207 + }
  208 +
  209 + $this->assign($arrNav);
  210 +
  211 + $navs = $navMenuModel->selectNavs();
  212 + $this->assign('navs', $navs);
  213 +
  214 + $this->assign("nav_id", $intNavId);
  215 + $this->assign("parent_id", $intParentId);
  216 +
  217 + return $this->fetch();
  218 + }
  219 +
  220 + /**
  221 + * 编辑导航菜单提交保存
  222 + * @adminMenu(
  223 + * 'name' => '编辑导航菜单提交保存',
  224 + * 'parent' => 'index',
  225 + * 'display'=> false,
  226 + * 'hasView'=> false,
  227 + * 'order' => 10000,
  228 + * 'icon' => '',
  229 + * 'remark' => '编辑导航菜单提交保存',
  230 + * 'param' => ''
  231 + * )
  232 + */
  233 + public function editPost()
  234 + {
  235 + $navMenuModel = new NavMenuModel();
  236 + $intId = $this->request->param('id', 0, 'intval');
  237 + $arrData = $this->request->post();
  238 +
  239 + if (isset($arrData['external_href'])) {
  240 + $arrData['href'] = htmlspecialchars_decode($arrData['external_href']);
  241 + } else {
  242 + $arrData['href'] = htmlspecialchars_decode($arrData['href']);
  243 + $arrData['href'] = base64_decode($arrData['href']);
  244 + }
  245 +
  246 + $navMenuModel->update($arrData, ["id" => $intId], true);
  247 +
  248 + $this->success(lang("EDIT_SUCCESS"), url("NavMenu/index", ['nav_id' => $arrData['nav_id']]));
  249 +
  250 + }
  251 +
  252 + /**
  253 + * 删除导航菜单
  254 + * @adminMenu(
  255 + * 'name' => '删除导航菜单',
  256 + * 'parent' => 'index',
  257 + * 'display'=> false,
  258 + * 'hasView'=> false,
  259 + * 'order' => 10000,
  260 + * 'icon' => '',
  261 + * 'remark' => '删除导航菜单',
  262 + * 'param' => ''
  263 + * )
  264 + */
  265 + public function delete()
  266 + {
  267 + $navMenuModel = new NavMenuModel();
  268 +
  269 + $intId = $this->request->param("id", 0, "intval");
  270 + $intNavId = $this->request->param("nav_id", 0, "intval");
  271 +
  272 + if (empty($intId)) {
  273 + $this->error(lang("NO_ID"));
  274 + }
  275 +
  276 + $count = $navMenuModel->where("parent_id", $intId)->count();
  277 + if ($count > 0) {
  278 + $this->error("该菜单下还有子菜单,无法删除!");
  279 + }
  280 +
  281 + $navMenuModel->where("id", $intId)->delete();
  282 + $this->success(lang("DELETE_SUCCESS"), url("NavMenu/index", ['nav_id' => $intNavId]));
  283 +
  284 + }
  285 +
  286 + /**
  287 + * 导航菜单排序
  288 + * @adminMenu(
  289 + * 'name' => '导航菜单排序',
  290 + * 'parent' => 'index',
  291 + * 'display'=> false,
  292 + * 'hasView'=> false,
  293 + * 'order' => 10000,
  294 + * 'icon' => '',
  295 + * 'remark' => '导航菜单排序',
  296 + * 'param' => ''
  297 + * )
  298 + */
  299 + public function listOrder()
  300 + {
  301 + $navMenuModel = new NavMenuModel();
  302 + $status = parent::listOrders($navMenuModel);
  303 + if ($status) {
  304 + $this->success("排序更新成功!");
  305 + } else {
  306 + $this->error("排序更新失败!");
  307 + }
  308 + }
  309 +
  310 +
  311 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <zxxjjforever@163.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use app\admin\model\PluginModel;
  15 +use app\admin\model\HookPluginModel;
  16 +use mindplay\annotations\Annotations;
  17 +use think\Db;
  18 +use think\facade\Cache;
  19 +use think\Validate;
  20 +
  21 +/**
  22 + * Class PluginController
  23 + * @package app\admin\controller
  24 + * @adminMenuRoot(
  25 + * 'name' =>'插件中心',
  26 + * 'action' =>'default',
  27 + * 'parent' =>'',
  28 + * 'display'=> true,
  29 + * 'order' => 20,
  30 + * 'icon' =>'cloud',
  31 + * 'remark' =>'插件中心'
  32 + * )
  33 + */
  34 +class PluginController extends AdminBaseController
  35 +{
  36 +
  37 + protected $pluginModel;
  38 +
  39 + /**
  40 + * 插件列表
  41 + * @adminMenu(
  42 + * 'name' => '插件列表',
  43 + * 'parent' => 'admin/Plugin/default',
  44 + * 'display'=> true,
  45 + * 'hasView'=> true,
  46 + * 'order' => 10000,
  47 + * 'icon' => '',
  48 + * 'remark' => '插件列表',
  49 + * 'param' => ''
  50 + * )
  51 + */
  52 + public function index()
  53 + {
  54 + $pluginModel = new PluginModel();
  55 + $plugins = $pluginModel->getList();
  56 + $this->assign("plugins", $plugins);
  57 + return $this->fetch();
  58 + }
  59 +
  60 + /**
  61 + * 插件启用/禁用
  62 + * @adminMenu(
  63 + * 'name' => '插件启用禁用',
  64 + * 'parent' => 'index',
  65 + * 'display'=> false,
  66 + * 'hasView'=> false,
  67 + * 'order' => 10000,
  68 + * 'icon' => '',
  69 + * 'remark' => '插件启用禁用',
  70 + * 'param' => ''
  71 + * )
  72 + */
  73 + public function toggle()
  74 + {
  75 + $id = $this->request->param('id', 0, 'intval');
  76 +
  77 + $pluginModel = PluginModel::get($id);
  78 +
  79 + if (empty($pluginModel)) {
  80 + $this->error('插件不存在!');
  81 + }
  82 +
  83 + $status = 1;
  84 + $successMessage = "启用成功!";
  85 +
  86 + if ($this->request->param('disable')) {
  87 + $status = 0;
  88 + $successMessage = "禁用成功!";
  89 + }
  90 +
  91 + $pluginModel->startTrans();
  92 +
  93 + try {
  94 + $pluginModel->save(['status' => $status], ['id' => $id]);
  95 +
  96 + $hookPluginModel = new HookPluginModel();
  97 +
  98 + $hookPluginModel->save(['status' => $status], ['plugin' => $pluginModel->name]);
  99 +
  100 + $pluginModel->commit();
  101 +
  102 + } catch (\Exception $e) {
  103 +
  104 + $pluginModel->rollback();
  105 +
  106 + $this->error('操作失败!');
  107 +
  108 + }
  109 +
  110 + Cache::clear('init_hook_plugins');
  111 +
  112 + $this->success($successMessage);
  113 + }
  114 +
  115 + /**
  116 + * 插件设置
  117 + * @adminMenu(
  118 + * 'name' => '插件设置',
  119 + * 'parent' => 'index',
  120 + * 'display'=> false,
  121 + * 'hasView'=> true,
  122 + * 'order' => 10000,
  123 + * 'icon' => '',
  124 + * 'remark' => '插件设置',
  125 + * 'param' => ''
  126 + * )
  127 + */
  128 + public function setting()
  129 + {
  130 + $id = $this->request->param('id', 0, 'intval');
  131 +
  132 + $pluginModel = new PluginModel();
  133 + $plugin = $pluginModel->find($id);
  134 +
  135 + if (empty($plugin)) {
  136 + $this->error('插件未安装!');
  137 + }
  138 +
  139 + $plugin = $plugin->toArray();
  140 +
  141 + $pluginClass = cmf_get_plugin_class($plugin['name']);
  142 + if (!class_exists($pluginClass)) {
  143 + $this->error('插件不存在!');
  144 + }
  145 +
  146 + $pluginObj = new $pluginClass;
  147 + //$plugin['plugin_path'] = $pluginObj->plugin_path;
  148 + //$plugin['custom_config'] = $pluginObj->custom_config;
  149 + $pluginConfigInDb = $plugin['config'];
  150 + $plugin['config'] = include $pluginObj->getConfigFilePath();
  151 +
  152 + if ($pluginConfigInDb) {
  153 + $pluginConfigInDb = json_decode($pluginConfigInDb, true);
  154 + foreach ($plugin['config'] as $key => $value) {
  155 + if ($value['type'] != 'group') {
  156 + if (isset($pluginConfigInDb[$key])) {
  157 + $plugin['config'][$key]['value'] = $pluginConfigInDb[$key];
  158 + }
  159 + } else {
  160 + foreach ($value['options'] as $group => $options) {
  161 + foreach ($options['options'] as $gkey => $value) {
  162 + if (isset($pluginConfigInDb[$gkey])) {
  163 + $plugin['config'][$key]['options'][$group]['options'][$gkey]['value'] = $pluginConfigInDb[$gkey];
  164 + }
  165 + }
  166 + }
  167 + }
  168 + }
  169 + }
  170 +
  171 + $this->assign('data', $plugin);
  172 +// if ($plugin['custom_config']) {
  173 +// $this->assign('custom_config', $this->fetch($plugin['plugin_path'] . $plugin['custom_config']));
  174 +// }
  175 +
  176 + $this->assign('id', $id);
  177 + return $this->fetch();
  178 +
  179 + }
  180 +
  181 + /**
  182 + * 插件设置提交
  183 + * @adminMenu(
  184 + * 'name' => '插件设置提交',
  185 + * 'parent' => 'index',
  186 + * 'display'=> false,
  187 + * 'hasView'=> false,
  188 + * 'order' => 10000,
  189 + * 'icon' => '',
  190 + * 'remark' => '插件设置提交',
  191 + * 'param' => ''
  192 + * )
  193 + */
  194 + public function settingPost()
  195 + {
  196 + if ($this->request->isPost()) {
  197 + $id = $this->request->param('id', 0, 'intval');
  198 +
  199 + $pluginModel = new PluginModel();
  200 + $plugin = $pluginModel->find($id)->toArray();
  201 +
  202 + if (!$plugin) {
  203 + $this->error('插件未安装!');
  204 + }
  205 +
  206 + $pluginClass = cmf_get_plugin_class($plugin['name']);
  207 + if (!class_exists($pluginClass)) {
  208 + $this->error('插件不存在!');
  209 + }
  210 +
  211 + $pluginObj = new $pluginClass;
  212 + //$plugin['plugin_path'] = $pluginObj->plugin_path;
  213 + //$plugin['custom_config'] = $pluginObj->custom_config;
  214 + $pluginConfigInDb = $plugin['config'];
  215 + $plugin['config'] = include $pluginObj->getConfigFilePath();
  216 +
  217 + $rules = [];
  218 + $messages = [];
  219 +
  220 + foreach ($plugin['config'] as $key => $value) {
  221 + if ($value['type'] != 'group') {
  222 + if (isset($value['rule'])) {
  223 + $rules[$key] = $this->_parseRules($value['rule']);
  224 + }
  225 +
  226 + if (isset($value['message'])) {
  227 + foreach ($value['message'] as $rule => $msg) {
  228 + $messages[$key . '.' . $rule] = $msg;
  229 + }
  230 + }
  231 +
  232 + } else {
  233 + foreach ($value['options'] as $group => $options) {
  234 + foreach ($options['options'] as $gkey => $value) {
  235 + if (isset($value['rule'])) {
  236 + $rules[$gkey] = $this->_parseRules($value['rule']);
  237 + }
  238 +
  239 + if (isset($value['message'])) {
  240 + foreach ($value['message'] as $rule => $msg) {
  241 + $messages[$gkey . '.' . $rule] = $msg;
  242 + }
  243 + }
  244 + }
  245 + }
  246 + }
  247 + }
  248 +
  249 + $config = $this->request->param('config/a');
  250 +
  251 + $validate = new Validate($rules, $messages);
  252 + $result = $validate->check($config);
  253 + if ($result !== true) {
  254 + $this->error($validate->getError());
  255 + }
  256 +
  257 + $pluginModel = new PluginModel();
  258 + $pluginModel->save(['config' => json_encode($config)], ['id' => $id]);
  259 + $this->success('保存成功', '');
  260 + }
  261 + }
  262 +
  263 + /**
  264 + * 解析插件配置验证规则
  265 + * @param $rules
  266 + * @return array
  267 + */
  268 + private function _parseRules($rules)
  269 + {
  270 + $newRules = [];
  271 +
  272 + $simpleRules = [
  273 + 'require', 'number',
  274 + 'integer', 'float', 'boolean', 'email',
  275 + 'array', 'accepted', 'date', 'alpha',
  276 + 'alphaNum', 'alphaDash', 'activeUrl',
  277 + 'url', 'ip'];
  278 + foreach ($rules as $key => $rule) {
  279 + if (in_array($key, $simpleRules) && $rule) {
  280 + array_push($newRules, $key);
  281 + }
  282 + }
  283 +
  284 + return $newRules;
  285 + }
  286 +
  287 + /**
  288 + * 插件安装
  289 + * @adminMenu(
  290 + * 'name' => '插件安装',
  291 + * 'parent' => 'index',
  292 + * 'display'=> false,
  293 + * 'hasView'=> false,
  294 + * 'order' => 10000,
  295 + * 'icon' => '',
  296 + * 'remark' => '插件安装',
  297 + * 'param' => ''
  298 + * )
  299 + */
  300 + public function install()
  301 + {
  302 + $pluginName = $this->request->param('name', '', 'trim');
  303 + $class = cmf_get_plugin_class($pluginName);
  304 + if (!class_exists($class)) {
  305 + $this->error('插件不存在!');
  306 + }
  307 +
  308 + $pluginModel = new PluginModel();
  309 + $pluginCount = $pluginModel->where('name', $pluginName)->count();
  310 +
  311 + if ($pluginCount > 0) {
  312 + $this->error('插件已安装!');
  313 + }
  314 +
  315 + $plugin = new $class;
  316 + $info = $plugin->info;
  317 + if (!$info || !$plugin->checkInfo()) {//检测信息的正确性
  318 + $this->error('插件信息缺失!');
  319 + }
  320 +
  321 + $installSuccess = $plugin->install();
  322 + if (!$installSuccess) {
  323 + $this->error('插件预安装失败!');
  324 + }
  325 +
  326 + $methods = get_class_methods($plugin);
  327 +
  328 + foreach ($methods as $methodKey => $method) {
  329 + $methods[$methodKey] = cmf_parse_name($method);
  330 + }
  331 +
  332 + $systemHooks = $pluginModel->getHooks(true);
  333 +
  334 + $pluginHooks = array_intersect($systemHooks, $methods);
  335 +
  336 + //$info['hooks'] = implode(",", $pluginHooks);
  337 +
  338 + if (!empty($plugin->hasAdmin)) {
  339 + $info['has_admin'] = 1;
  340 + } else {
  341 + $info['has_admin'] = 0;
  342 + }
  343 +
  344 + $info['config'] = json_encode($plugin->getConfig());
  345 +
  346 + $pluginModel->data($info)->allowField(true)->save();
  347 +
  348 + $hookPluginModel = new HookPluginModel();
  349 + foreach ($pluginHooks as $pluginHook) {
  350 + $hookPluginModel->data(['hook' => $pluginHook, 'plugin' => $pluginName, 'status' => 1])->isUpdate(false)->save();
  351 + }
  352 +
  353 + $this->_getActions($pluginName);
  354 +
  355 + Cache::clear('init_hook_plugins');
  356 + Cache::clear('admin_menus');// 删除后台菜单缓存
  357 +
  358 + $this->success('安装成功!');
  359 + }
  360 +
  361 + /**
  362 + * 插件更新
  363 + * @adminMenu(
  364 + * 'name' => '插件更新',
  365 + * 'parent' => 'index',
  366 + * 'display'=> false,
  367 + * 'hasView'=> false,
  368 + * 'order' => 10000,
  369 + * 'icon' => '',
  370 + * 'remark' => '插件更新',
  371 + * 'param' => ''
  372 + * )
  373 + */
  374 + public function update()
  375 + {
  376 + $pluginName = $this->request->param('name', '', 'trim');
  377 + $class = cmf_get_plugin_class($pluginName);
  378 + if (!class_exists($class)) {
  379 + $this->error('插件不存在!');
  380 + }
  381 +
  382 + $plugin = new $class;
  383 + $info = $plugin->info;
  384 + if (!$info || !$plugin->checkInfo()) {//检测信息的正确性
  385 + $this->error('插件信息缺失!');
  386 + }
  387 +
  388 + $methods = get_class_methods($plugin);
  389 +
  390 + foreach ($methods as $methodKey => $method) {
  391 + $methods[$methodKey] = cmf_parse_name($method);
  392 + }
  393 +
  394 + $pluginModel = new PluginModel();
  395 + $systemHooks = $pluginModel->getHooks(true);
  396 +
  397 + $pluginHooks = array_intersect($systemHooks, $methods);
  398 +
  399 + if (!empty($plugin->hasAdmin)) {
  400 + $info['has_admin'] = 1;
  401 + } else {
  402 + $info['has_admin'] = 0;
  403 + }
  404 +
  405 + $config = $plugin->getConfig();
  406 +
  407 + $defaultConfig = $plugin->getDefaultConfig();
  408 +
  409 + $pluginModel = new PluginModel();
  410 +
  411 + $config = array_merge($defaultConfig, $config);
  412 +
  413 + $info['config'] = json_encode($config);
  414 +
  415 + $pluginModel->allowField(true)->save($info, ['name' => $pluginName]);
  416 +
  417 + $hookPluginModel = new HookPluginModel();
  418 +
  419 + $pluginHooksInDb = $hookPluginModel->where('plugin', $pluginName)->column('hook');
  420 +
  421 + $samePluginHooks = array_intersect($pluginHooks, $pluginHooksInDb);
  422 +
  423 + $shouldDeleteHooks = array_diff($samePluginHooks, $pluginHooksInDb);
  424 +
  425 + $newHooks = array_diff($pluginHooks, $samePluginHooks);
  426 +
  427 + if (count($shouldDeleteHooks) > 0) {
  428 + $hookPluginModel->where('hook', 'in', $shouldDeleteHooks)->delete();
  429 + }
  430 +
  431 + foreach ($newHooks as $pluginHook) {
  432 + $hookPluginModel->data(['hook' => $pluginHook, 'plugin' => $pluginName])->isUpdate(false)->save();
  433 + }
  434 +
  435 + $this->_getActions($pluginName);
  436 +
  437 + Cache::clear('init_hook_plugins');
  438 + Cache::clear('admin_menus');// 删除后台菜单缓存
  439 +
  440 + $this->success('更新成功!');
  441 + }
  442 +
  443 + private function _getActions($pluginName)
  444 + {
  445 + Annotations::$config['cache'] = false;
  446 + $annotationManager = Annotations::getManager();
  447 + $annotationManager->registry['adminMenu'] = 'app\admin\annotation\AdminMenuAnnotation';
  448 + $annotationManager->registry['adminMenuRoot'] = 'app\admin\annotation\AdminMenuRootAnnotation';
  449 + $newMenus = [];
  450 +
  451 + $pluginDir = cmf_parse_name($pluginName);
  452 +
  453 + $filePatten = WEB_ROOT . 'plugins/' . $pluginDir . '/controller/Admin*Controller.php';
  454 +
  455 + $controllers = cmf_scan_dir($filePatten);
  456 +
  457 + $app = 'plugin/' . $pluginName;
  458 +
  459 + if (!empty($controllers)) {
  460 + foreach ($controllers as $controller) {
  461 + $controller = preg_replace('/\.php$/', '', $controller);
  462 + $controllerName = preg_replace('/\Controller$/', '', $controller);
  463 + $controllerClass = "plugins\\$pluginDir\\controller\\$controller";
  464 +
  465 + $menuAnnotations = Annotations::ofClass($controllerClass, '@adminMenuRoot');
  466 +
  467 + if (!empty($menuAnnotations)) {
  468 + foreach ($menuAnnotations as $menuAnnotation) {
  469 +
  470 + $name = $menuAnnotation->name;
  471 + $icon = $menuAnnotation->icon;
  472 + $type = 0;//1:有界面可访问菜单,2:无界面可访问菜单,0:只作为菜单
  473 + $action = $menuAnnotation->action;
  474 + $status = empty($menuAnnotation->display) ? 0 : 1;
  475 + $listOrder = floatval($menuAnnotation->order);
  476 + $param = $menuAnnotation->param;
  477 + $remark = $menuAnnotation->remark;
  478 +
  479 + if (empty($menuAnnotation->parent)) {
  480 + $parentId = 0;
  481 + } else {
  482 +
  483 + $parent = explode('/', $menuAnnotation->parent);
  484 + $countParent = count($parent);
  485 + if ($countParent > 3) {
  486 + throw new \Exception($controllerClass . ':' . $action . ' @adminMenuRoot parent格式不正确!');
  487 + }
  488 +
  489 + $parentApp = $app;
  490 + $parentController = $controllerName;
  491 + $parentAction = '';
  492 +
  493 + switch ($countParent) {
  494 + case 1:
  495 + $parentAction = $parent[0];
  496 + break;
  497 + case 2:
  498 + $parentController = $parent[0];
  499 + $parentAction = $parent[1];
  500 + break;
  501 + case 3:
  502 + $parentApp = $parent[0];
  503 + $parentController = $parent[1];
  504 + $parentAction = $parent[2];
  505 + break;
  506 + }
  507 +
  508 + $findParentAdminMenu = Db::name('admin_menu')->where([
  509 + 'app' => $parentApp,
  510 + 'controller' => $parentController,
  511 + 'action' => $parentAction
  512 + ])->find();
  513 +
  514 + if (empty($findParentAdminMenu)) {
  515 + $parentId = Db::name('admin_menu')->insertGetId([
  516 + 'app' => $parentApp,
  517 + 'controller' => $parentController,
  518 + 'action' => $parentAction,
  519 + 'name' => '--new--'
  520 + ]);
  521 + } else {
  522 + $parentId = $findParentAdminMenu['id'];
  523 + }
  524 + }
  525 +
  526 + $findAdminMenu = Db::name('admin_menu')->where([
  527 + 'app' => $app,
  528 + 'controller' => $controllerName,
  529 + 'action' => $action
  530 + ])->find();
  531 +
  532 + if (empty($findAdminMenu)) {
  533 +
  534 + Db::name('admin_menu')->insert([
  535 + 'parent_id' => $parentId,
  536 + 'type' => $type,
  537 + 'status' => $status,
  538 + 'list_order' => $listOrder,
  539 + 'app' => $app,
  540 + 'controller' => $controllerName,
  541 + 'action' => $action,
  542 + 'param' => $param,
  543 + 'name' => $name,
  544 + 'icon' => $icon,
  545 + 'remark' => $remark
  546 + ]);
  547 +
  548 + $menuName = $name;
  549 +
  550 +// array_push($newMenus, $app . "/$controllerName/$action 已导入");
  551 +
  552 + } else {
  553 +
  554 + if ($findAdminMenu['name'] == '--new--') {
  555 + Db::name('admin_menu')->where([
  556 + 'app' => $app,
  557 + 'controller' => $controllerName,
  558 + 'action' => $action
  559 + ])->update([
  560 + 'parent_id' => $parentId,
  561 + 'type' => $type,
  562 + 'status' => $status,
  563 + 'list_order' => $listOrder,
  564 + 'param' => $param,
  565 + 'name' => $name,
  566 + 'icon' => $icon,
  567 + 'remark' => $remark
  568 + ]);
  569 + $menuName = $name;
  570 + } else {
  571 + // 只关注菜单层级关系,是否有视图
  572 + Db::name('admin_menu')->where([
  573 + 'app' => $app,
  574 + 'controller' => $controllerName,
  575 + 'action' => $action
  576 + ])->update([
  577 + //'parent_id' => $parentId,
  578 + 'type' => $type,
  579 + ]);
  580 + $menuName = $findAdminMenu['name'];
  581 + }
  582 +
  583 +// array_push($newMenus, $app."/$controllerName/$action 层级关系已更新");
  584 + }
  585 +
  586 + $authRuleName = "plugin/{$pluginName}/{$controllerName}/{$action}";
  587 + $findAuthRuleCount = Db::name('auth_rule')->where([
  588 + 'app' => $app,
  589 + 'name' => $authRuleName,
  590 + 'type' => 'admin_url'
  591 + ])->count();
  592 +
  593 + if ($findAuthRuleCount == 0) {
  594 + Db::name('auth_rule')->insert([
  595 + 'app' => $app,
  596 + 'name' => $authRuleName,
  597 + 'type' => 'admin_url',
  598 + 'param' => $param,
  599 + 'title' => $menuName
  600 + ]);
  601 + } else {
  602 + Db::name('auth_rule')->where([
  603 + 'app' => $app,
  604 + 'name' => $authRuleName,
  605 + 'type' => 'admin_url',
  606 + ])->update([
  607 + 'param' => $param,
  608 + 'title' => $menuName
  609 + ]);
  610 + }
  611 +
  612 + }
  613 + }
  614 +
  615 + $reflect = new \ReflectionClass($controllerClass);
  616 + $methods = $reflect->getMethods(\ReflectionMethod::IS_PUBLIC);
  617 +
  618 + if (!empty($methods)) {
  619 + foreach ($methods as $method) {
  620 +
  621 + if ($method->class == $controllerClass && strpos($method->name, '_') !== 0) {
  622 + $menuAnnotations = Annotations::ofMethod($controllerClass, $method->name, '@adminMenu');
  623 +
  624 + if (!empty($menuAnnotations)) {
  625 +
  626 + $menuAnnotation = $menuAnnotations[0];
  627 +
  628 + $name = $menuAnnotation->name;
  629 + $icon = $menuAnnotation->icon;
  630 + $type = $menuAnnotation->hasView ? 1 : 2;//1:有界面可访问菜单,2:无界面可访问菜单,0:只作为菜单
  631 + $action = $method->name;
  632 + $status = empty($menuAnnotation->display) ? 0 : 1;
  633 + $listOrder = floatval($menuAnnotation->order);
  634 + $param = $menuAnnotation->param;
  635 + $remark = $menuAnnotation->remark;
  636 +
  637 + if (empty($menuAnnotation->parent)) {
  638 + $parentId = 0;
  639 + } else {
  640 + $parent = explode('/', $menuAnnotation->parent);
  641 + $countParent = count($parent);
  642 + if ($countParent > 3) {
  643 + throw new \Exception($controllerClass . ':' . $action . ' @menuRoot parent格式不正确!');
  644 + }
  645 +
  646 + $parentApp = $app;
  647 + $parentController = $controllerName;
  648 + $parentAction = '';
  649 +
  650 + switch ($countParent) {
  651 + case 1:
  652 + $parentAction = $parent[0];
  653 + break;
  654 + case 2:
  655 + $parentController = $parent[0];
  656 + $parentAction = $parent[1];
  657 + break;
  658 + case 3:
  659 + $parentApp = $parent[0];
  660 + $parentController = $parent[1];
  661 + $parentAction = $parent[2];
  662 + break;
  663 + }
  664 +
  665 + $findParentAdminMenu = Db::name('admin_menu')->where([
  666 + 'app' => $parentApp,
  667 + 'controller' => $parentController,
  668 + 'action' => $parentAction
  669 + ])->find();
  670 +
  671 + if (empty($findParentAdminMenu)) {
  672 + $parentId = Db::name('admin_menu')->insertGetId([
  673 + 'app' => $parentApp,
  674 + 'controller' => $parentController,
  675 + 'action' => $parentAction,
  676 + 'name' => '--new--'
  677 + ]);
  678 + } else {
  679 + $parentId = $findParentAdminMenu['id'];
  680 + }
  681 + }
  682 +
  683 + $findAdminMenu = Db::name('admin_menu')->where([
  684 + 'app' => $app,
  685 + 'controller' => $controllerName,
  686 + 'action' => $action
  687 + ])->find();
  688 +
  689 + if (empty($findAdminMenu)) {
  690 +
  691 + Db::name('admin_menu')->insert([
  692 + 'parent_id' => $parentId,
  693 + 'type' => $type,
  694 + 'status' => $status,
  695 + 'list_order' => $listOrder,
  696 + 'app' => $app,
  697 + 'controller' => $controllerName,
  698 + 'action' => $action,
  699 + 'param' => $param,
  700 + 'name' => $name,
  701 + 'icon' => $icon,
  702 + 'remark' => $remark
  703 + ]);
  704 +
  705 + $menuName = $name;
  706 +
  707 + //array_push($newMenus, "$app/$controllerName/$action 已导入");
  708 +
  709 + } else {
  710 + if ($findAdminMenu['name'] == '--new--') {
  711 + Db::name('admin_menu')->where([
  712 + 'app' => $app,
  713 + 'controller' => $controllerName,
  714 + 'action' => $action
  715 + ])->update([
  716 + 'parent_id' => $parentId,
  717 + 'type' => $type,
  718 + 'status' => $status,
  719 + 'list_order' => $listOrder,
  720 + 'param' => $param,
  721 + 'name' => $name,
  722 + 'icon' => $icon,
  723 + 'remark' => $remark
  724 + ]);
  725 + $menuName = $name;
  726 + } else {
  727 + // 只关注是否有视图
  728 + Db::name('admin_menu')->where([
  729 + 'app' => $app,
  730 + 'controller' => $controllerName,
  731 + 'action' => $action
  732 + ])->update([
  733 + //'parent_id' => $parentId,
  734 + 'type' => $type,
  735 + ]);
  736 + $menuName = $findAdminMenu['name'];
  737 + }
  738 +
  739 +
  740 +// array_push($newMenus, "$app/$controllerName/$action 已更新");
  741 + }
  742 +
  743 + $authRuleName = "plugin/{$pluginName}/{$controllerName}/{$action}";
  744 + $findAuthRuleCount = Db::name('auth_rule')->where([
  745 + 'app' => $app,
  746 + 'name' => $authRuleName,
  747 + 'type' => 'plugin_url'
  748 + ])->count();
  749 +
  750 + if ($findAuthRuleCount == 0) {
  751 + Db::name('auth_rule')->insert([
  752 + 'app' => $app,
  753 + 'name' => $authRuleName,
  754 + 'type' => 'plugin_url',
  755 + 'param' => $param,
  756 + 'title' => $menuName
  757 + ]);
  758 + } else {
  759 + Db::name('auth_rule')->where([
  760 + 'app' => $app,
  761 + 'name' => $authRuleName,
  762 + 'type' => 'plugin_url',
  763 + ])->update([
  764 + 'param' => $param,
  765 + 'title' => $menuName
  766 + ]);
  767 + }
  768 + }
  769 +
  770 + }
  771 + }
  772 + }
  773 +
  774 + }
  775 + }
  776 +
  777 + }
  778 +
  779 + /**
  780 + * 卸载插件
  781 + * @adminMenu(
  782 + * 'name' => '卸载插件',
  783 + * 'parent' => 'index',
  784 + * 'display'=> false,
  785 + * 'hasView'=> false,
  786 + * 'order' => 10000,
  787 + * 'icon' => '',
  788 + * 'remark' => '卸载插件',
  789 + * 'param' => ''
  790 + * )
  791 + */
  792 + public function uninstall()
  793 + {
  794 + $pluginModel = new PluginModel();
  795 + $id = $this->request->param('id', 0, 'intval');
  796 +
  797 + $result = $pluginModel->uninstall($id);
  798 +
  799 + if ($result !== true) {
  800 + $this->error('卸载失败!');
  801 + }
  802 +
  803 + Cache::clear('init_hook_plugins');
  804 + Cache::clear('admin_menus');// 删除后台菜单缓存
  805 +
  806 + $this->success('卸载成功!');
  807 + }
  808 +
  809 +
  810 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use think\Db;
  15 +
  16 +class PublicController extends AdminBaseController
  17 +{
  18 + public function initialize()
  19 + {
  20 + }
  21 +
  22 + /**
  23 + * 后台登陆界面
  24 + */
  25 + public function login()
  26 + {
  27 + $loginAllowed = session("__LOGIN_BY_CMF_ADMIN_PW__");
  28 + if (empty($loginAllowed)) {
  29 + //$this->error('非法登录!', cmf_get_root() . '/');
  30 + return redirect(cmf_get_root() . "/");
  31 + }
  32 +
  33 + $admin_id = session('ADMIN_ID');
  34 + if (!empty($admin_id)) {//已经登录
  35 + return redirect(url("admin/Index/index"));
  36 + } else {
  37 + session("__SP_ADMIN_LOGIN_PAGE_SHOWED_SUCCESS__", true);
  38 + $result = hook_one('admin_login');
  39 + if (!empty($result)) {
  40 + return $result;
  41 + }
  42 + return $this->fetch(":login");
  43 + }
  44 + }
  45 +
  46 + /**
  47 + * 登录验证
  48 + */
  49 + public function doLogin()
  50 + {
  51 + if (hook_one('admin_custom_login_open')) {
  52 + $this->error('您已经通过插件自定义后台登录!');
  53 + }
  54 +
  55 + $loginAllowed = session("__LOGIN_BY_CMF_ADMIN_PW__");
  56 + if (empty($loginAllowed)) {
  57 + $this->error('非法登录!', cmf_get_root() . '/');
  58 + }
  59 +
  60 + $captcha = $this->request->param('captcha');
  61 + if (empty($captcha)) {
  62 + $this->error(lang('CAPTCHA_REQUIRED'));
  63 + }
  64 + //验证码
  65 + if (!cmf_captcha_check($captcha)) {
  66 + $this->error(lang('CAPTCHA_NOT_RIGHT'));
  67 + }
  68 +
  69 + $name = $this->request->param("username");
  70 + if (empty($name)) {
  71 + $this->error(lang('USERNAME_OR_EMAIL_EMPTY'));
  72 + }
  73 + $pass = $this->request->param("password");
  74 + if (empty($pass)) {
  75 + $this->error(lang('PASSWORD_REQUIRED'));
  76 + }
  77 + if (strpos($name, "@") > 0) {//邮箱登陆
  78 + $where['user_email'] = $name;
  79 + } else {
  80 + $where['user_login'] = $name;
  81 + }
  82 +
  83 + $result = Db::name('user')->where($where)->find();
  84 +
  85 + if (!empty($result) && $result['user_type'] == 1) {
  86 + if (cmf_compare_password($pass, $result['user_pass'])) {
  87 + $groups = Db::name('RoleUser')
  88 + ->alias("a")
  89 + ->join('__ROLE__ b', 'a.role_id =b.id')
  90 + ->where(["user_id" => $result["id"], "status" => 1])
  91 + ->value("role_id");
  92 + if ($result["id"] != 1 && (empty($groups) || empty($result['user_status']))) {
  93 + $this->error(lang('USE_DISABLED'));
  94 + }
  95 + //登入成功页面跳转
  96 + session('ADMIN_ID', $result["id"]);
  97 + session('name', $result["user_login"]);
  98 + $result['last_login_ip'] = get_client_ip(0, true);
  99 + $result['last_login_time'] = time();
  100 + $token = cmf_generate_user_token($result["id"], 'web');
  101 + if (!empty($token)) {
  102 + session('token', $token);
  103 + }
  104 + Db::name('user')->update($result);
  105 + cookie("admin_username", $name, 3600 * 24 * 30);
  106 + session("__LOGIN_BY_CMF_ADMIN_PW__", null);
  107 + $this->success(lang('LOGIN_SUCCESS'), url("admin/Index/index"));
  108 + } else {
  109 + $this->error(lang('PASSWORD_NOT_RIGHT'));
  110 + }
  111 + } else {
  112 + $this->error(lang('USERNAME_NOT_EXIST'));
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * 后台管理员退出
  118 + */
  119 + public function logout()
  120 + {
  121 + session('ADMIN_ID', null);
  122 + return redirect(url('/', [], false, true));
  123 + }
  124 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use cmf\controller\AdminBaseController;
  14 +use think\Db;
  15 +use think\facade\Cache;
  16 +use tree\Tree;
  17 +use app\admin\model\AdminMenuModel;
  18 +
  19 +class RbacController extends AdminBaseController
  20 +{
  21 +
  22 + /**
  23 + * 角色管理列表
  24 + * @adminMenu(
  25 + * 'name' => '角色管理',
  26 + * 'parent' => 'admin/User/default',
  27 + * 'display'=> true,
  28 + * 'hasView'=> true,
  29 + * 'order' => 10000,
  30 + * 'icon' => '',
  31 + * 'remark' => '角色管理',
  32 + * 'param' => ''
  33 + * )
  34 + * @return mixed
  35 + * @throws \think\db\exception\DataNotFoundException
  36 + * @throws \think\db\exception\ModelNotFoundException
  37 + * @throws \think\exception\DbException
  38 + */
  39 + public function index()
  40 + {
  41 + $content = hook_one('admin_rbac_index_view');
  42 +
  43 + if (!empty($content)) {
  44 + return $content;
  45 + }
  46 +
  47 + $data = Db::name('role')->order(["list_order" => "ASC", "id" => "DESC"])->select();
  48 + $this->assign("roles", $data);
  49 + return $this->fetch();
  50 + }
  51 +
  52 + /**
  53 + * 添加角色
  54 + * @adminMenu(
  55 + * 'name' => '添加角色',
  56 + * 'parent' => 'index',
  57 + * 'display'=> false,
  58 + * 'hasView'=> true,
  59 + * 'order' => 10000,
  60 + * 'icon' => '',
  61 + * 'remark' => '添加角色',
  62 + * 'param' => ''
  63 + * )
  64 + * @return mixed
  65 + */
  66 + public function roleAdd()
  67 + {
  68 + $content = hook_one('admin_rbac_role_add_view');
  69 +
  70 + if (!empty($content)) {
  71 + return $content;
  72 + }
  73 +
  74 + return $this->fetch();
  75 + }
  76 +
  77 + /**
  78 + * 添加角色提交
  79 + * @adminMenu(
  80 + * 'name' => '添加角色提交',
  81 + * 'parent' => 'index',
  82 + * 'display'=> false,
  83 + * 'hasView'=> false,
  84 + * 'order' => 10000,
  85 + * 'icon' => '',
  86 + * 'remark' => '添加角色提交',
  87 + * 'param' => ''
  88 + * )
  89 + */
  90 + public function roleAddPost()
  91 + {
  92 + if ($this->request->isPost()) {
  93 + $data = $this->request->param();
  94 + $result = $this->validate($data, 'role');
  95 + if ($result !== true) {
  96 + // 验证失败 输出错误信息
  97 + $this->error($result);
  98 + } else {
  99 + $result = Db::name('role')->insert($data);
  100 + if ($result) {
  101 + $this->success("添加角色成功", url("rbac/index"));
  102 + } else {
  103 + $this->error("添加角色失败");
  104 + }
  105 +
  106 + }
  107 + }
  108 + }
  109 +
  110 + /**
  111 + * 编辑角色
  112 + * @adminMenu(
  113 + * 'name' => '编辑角色',
  114 + * 'parent' => 'index',
  115 + * 'display'=> false,
  116 + * 'hasView'=> true,
  117 + * 'order' => 10000,
  118 + * 'icon' => '',
  119 + * 'remark' => '编辑角色',
  120 + * 'param' => ''
  121 + * )
  122 + * @return mixed
  123 + * @throws \think\db\exception\DataNotFoundException
  124 + * @throws \think\db\exception\ModelNotFoundException
  125 + * @throws \think\exception\DbException
  126 + */
  127 + public function roleEdit()
  128 + {
  129 + $content = hook_one('admin_rbac_role_edit_view');
  130 +
  131 + if (!empty($content)) {
  132 + return $content;
  133 + }
  134 +
  135 + $id = $this->request->param("id", 0, 'intval');
  136 + if ($id == 1) {
  137 + $this->error("超级管理员角色不能被修改!");
  138 + }
  139 + $data = Db::name('role')->where("id", $id)->find();
  140 + if (!$data) {
  141 + $this->error("该角色不存在!");
  142 + }
  143 + $this->assign("data", $data);
  144 + return $this->fetch();
  145 + }
  146 +
  147 + /**
  148 + * 编辑角色提交
  149 + * @adminMenu(
  150 + * 'name' => '编辑角色提交',
  151 + * 'parent' => 'index',
  152 + * 'display'=> false,
  153 + * 'hasView'=> false,
  154 + * 'order' => 10000,
  155 + * 'icon' => '',
  156 + * 'remark' => '编辑角色提交',
  157 + * 'param' => ''
  158 + * )
  159 + * @throws \think\Exception
  160 + * @throws \think\exception\PDOException
  161 + */
  162 + public function roleEditPost()
  163 + {
  164 + $id = $this->request->param("id", 0, 'intval');
  165 + if ($id == 1) {
  166 + $this->error("超级管理员角色不能被修改!");
  167 + }
  168 + if ($this->request->isPost()) {
  169 + $data = $this->request->param();
  170 + $result = $this->validate($data, 'role');
  171 + if ($result !== true) {
  172 + // 验证失败 输出错误信息
  173 + $this->error($result);
  174 +
  175 + } else {
  176 + if (Db::name('role')->update($data) !== false) {
  177 + $this->success("保存成功!", url('rbac/index'));
  178 + } else {
  179 + $this->error("保存失败!");
  180 + }
  181 + }
  182 + }
  183 + }
  184 +
  185 + /**
  186 + * 删除角色
  187 + * @adminMenu(
  188 + * 'name' => '删除角色',
  189 + * 'parent' => 'index',
  190 + * 'display'=> false,
  191 + * 'hasView'=> false,
  192 + * 'order' => 10000,
  193 + * 'icon' => '',
  194 + * 'remark' => '删除角色',
  195 + * 'param' => ''
  196 + * )
  197 + * @throws \think\Exception
  198 + * @throws \think\exception\PDOException
  199 + */
  200 + public function roleDelete()
  201 + {
  202 + $id = $this->request->param("id", 0, 'intval');
  203 + if ($id == 1) {
  204 + $this->error("超级管理员角色不能被删除!");
  205 + }
  206 + $count = Db::name('RoleUser')->where('role_id', $id)->count();
  207 + if ($count > 0) {
  208 + $this->error("该角色已经有用户!");
  209 + } else {
  210 + $status = Db::name('role')->delete($id);
  211 + if (!empty($status)) {
  212 + $this->success("删除成功!", url('rbac/index'));
  213 + } else {
  214 + $this->error("删除失败!");
  215 + }
  216 + }
  217 + }
  218 +
  219 + /**
  220 + * 设置角色权限
  221 + * @adminMenu(
  222 + * 'name' => '设置角色权限',
  223 + * 'parent' => 'index',
  224 + * 'display'=> false,
  225 + * 'hasView'=> true,
  226 + * 'order' => 10000,
  227 + * 'icon' => '',
  228 + * 'remark' => '设置角色权限',
  229 + * 'param' => ''
  230 + * )
  231 + * @return mixed
  232 + */
  233 + public function authorize()
  234 + {
  235 + $content = hook_one('admin_rbac_authorize_view');
  236 +
  237 + if (!empty($content)) {
  238 + return $content;
  239 + }
  240 +
  241 + $AuthAccess = Db::name("AuthAccess");
  242 + $adminMenuModel = new AdminMenuModel();
  243 + //角色ID
  244 + $roleId = $this->request->param("id", 0, 'intval');
  245 + if (empty($roleId)) {
  246 + $this->error("参数错误!");
  247 + }
  248 +
  249 + $tree = new Tree();
  250 + $tree->icon = ['│ ', '├─ ', '└─ '];
  251 + $tree->nbsp = '&nbsp;&nbsp;&nbsp;';
  252 +
  253 + $result = $adminMenuModel->menuCache();
  254 +
  255 + $newMenus = [];
  256 + $privilegeData = $AuthAccess->where("role_id", $roleId)->column("rule_name");//获取权限表数据
  257 +
  258 + foreach ($result as $m) {
  259 + $newMenus[$m['id']] = $m;
  260 + }
  261 +
  262 + foreach ($result as $n => $t) {
  263 + $result[$n]['checked'] = ($this->_isChecked($t, $privilegeData)) ? ' checked' : '';
  264 + $result[$n]['level'] = $this->_getLevel($t['id'], $newMenus);
  265 + $result[$n]['style'] = empty($t['parent_id']) ? '' : 'display:none;';
  266 + $result[$n]['parentIdNode'] = ($t['parent_id']) ? ' class="child-of-node-' . $t['parent_id'] . '"' : '';
  267 + }
  268 +
  269 + $str = "<tr id='node-\$id'\$parentIdNode style='\$style'>
  270 + <td style='padding-left:30px;'>\$spacer<input type='checkbox' name='menuId[]' value='\$id' level='\$level' \$checked onclick='javascript:checknode(this);'> \$name</td>
  271 + </tr>";
  272 + $tree->init($result);
  273 +
  274 + $category = $tree->getTree(0, $str);
  275 +
  276 + $this->assign("category", $category);
  277 + $this->assign("roleId", $roleId);
  278 + return $this->fetch();
  279 + }
  280 +
  281 + /**
  282 + * 角色授权提交
  283 + * @adminMenu(
  284 + * 'name' => '角色授权提交',
  285 + * 'parent' => 'index',
  286 + * 'display'=> false,
  287 + * 'hasView'=> false,
  288 + * 'order' => 10000,
  289 + * 'icon' => '',
  290 + * 'remark' => '角色授权提交',
  291 + * 'param' => ''
  292 + * )
  293 + * @throws \think\Exception
  294 + * @throws \think\db\exception\DataNotFoundException
  295 + * @throws \think\db\exception\ModelNotFoundException
  296 + * @throws \think\exception\DbException
  297 + * @throws \think\exception\PDOException
  298 + */
  299 + public function authorizePost()
  300 + {
  301 + if ($this->request->isPost()) {
  302 + $roleId = $this->request->param("roleId", 0, 'intval');
  303 + if (!$roleId) {
  304 + $this->error("需要授权的角色不存在!");
  305 + }
  306 + if (is_array($this->request->param('menuId/a')) && count($this->request->param('menuId/a')) > 0) {
  307 +
  308 + Db::name("authAccess")->where(["role_id" => $roleId, 'type' => 'admin_url'])->delete();
  309 + foreach ($_POST['menuId'] as $menuId) {
  310 + $menu = Db::name("adminMenu")->where("id", $menuId)->field("app,controller,action")->find();
  311 + if ($menu) {
  312 + $app = $menu['app'];
  313 + $model = $menu['controller'];
  314 + $action = $menu['action'];
  315 + $name = strtolower("$app/$model/$action");
  316 + Db::name("authAccess")->insert(["role_id" => $roleId, "rule_name" => $name, 'type' => 'admin_url']);
  317 + }
  318 + }
  319 +
  320 + Cache::clear('admin_menus');// 删除后台菜单缓存
  321 +
  322 + $this->success("授权成功!");
  323 + } else {
  324 + //当没有数据时,清除当前角色授权
  325 + Db::name("authAccess")->where("role_id", $roleId)->delete();
  326 + $this->error("没有接收到数据,执行清除授权成功!");
  327 + }
  328 + }
  329 + }
  330 +
  331 + /**
  332 + * 检查指定菜单是否有权限
  333 + * @param array $menu menu表中数组
  334 + * @param $privData
  335 + * @return bool
  336 + */
  337 + private function _isChecked($menu, $privData)
  338 + {
  339 + $app = $menu['app'];
  340 + $model = $menu['controller'];
  341 + $action = $menu['action'];
  342 + $name = strtolower("$app/$model/$action");
  343 + if ($privData) {
  344 + if (in_array($name, $privData)) {
  345 + return true;
  346 + } else {
  347 + return false;
  348 + }
  349 + } else {
  350 + return false;
  351 + }
  352 +
  353 + }
  354 +
  355 + /**
  356 + * 获取菜单深度
  357 + * @param $id
  358 + * @param array $array
  359 + * @param int $i
  360 + * @return int
  361 + */
  362 + protected function _getLevel($id, $array = [], $i = 0)
  363 + {
  364 + if ($array[$id]['parent_id'] == 0 || empty($array[$array[$id]['parent_id']]) || $array[$id]['parent_id'] == $id) {
  365 + return $i;
  366 + } else {
  367 + $i++;
  368 + return $this->_getLevel($array[$id]['parent_id'], $array, $i);
  369 + }
  370 + }
  371 +
  372 + //角色成员管理
  373 + public function member()
  374 + {
  375 + //TODO 添加角色成员管理
  376 +
  377 + }
  378 +
  379 +}
  380 +
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 小夏 < 449134904@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use app\admin\model\RecycleBinModel;
  14 +use app\admin\model\RouteModel;
  15 +use cmf\controller\AdminBaseController;
  16 +use think\Db;
  17 +
  18 +class RecycleBinController extends AdminBaseController
  19 +{
  20 + /**
  21 + * 回收站
  22 + * @adminMenu(
  23 + * 'name' => '回收站',
  24 + * 'parent' => '',
  25 + * 'display'=> false,
  26 + * 'hasView'=> true,
  27 + * 'order' => 10000,
  28 + * 'icon' => '',
  29 + * 'remark' => '回收站',
  30 + * 'param' => ''
  31 + * )
  32 + */
  33 + public function index()
  34 + {
  35 + $content = hook_one('admin_recycle_bin_index_view');
  36 +
  37 + if (!empty($content)) {
  38 + return $content;
  39 + }
  40 +
  41 + $recycleBinModel = new RecycleBinModel();
  42 + $list = $recycleBinModel->order('create_time desc')->paginate(10);
  43 + // 获取分页显示
  44 + $page = $list->render();
  45 + $this->assign('page', $page);
  46 + $this->assign('list', $list);
  47 + return $this->fetch();
  48 + }
  49 +
  50 + /**
  51 + * 回收站还原
  52 + * @adminMenu(
  53 + * 'name' => '回收站还原',
  54 + * 'parent' => 'index',
  55 + * 'display'=> false,
  56 + * 'hasView'=> false,
  57 + * 'order' => 10000,
  58 + * 'icon' => '',
  59 + * 'remark' => '回收站还原',
  60 + * 'param' => ''
  61 + * )
  62 + */
  63 + public function restore()
  64 + {
  65 +
  66 + $id = $this->request->param('id', 0, 'intval');
  67 + $result = Db::name('recycleBin')->where('id', $id)->find();
  68 +
  69 + $tableName = explode('#', $result['table_name']);
  70 + $tableName = $tableName[0];
  71 + //还原资源
  72 + if ($result) {
  73 + $res = Db::name($tableName)
  74 + ->where('id', $result['object_id'])
  75 + ->update(['delete_time' => '0']);
  76 + if ($tableName == 'portal_post') {
  77 + Db::name('portal_category_post')->where('post_id', $result['object_id'])->update(['status' => 1]);
  78 + Db::name('portal_tag_post')->where('post_id', $result['object_id'])->update(['status' => 1]);
  79 + }
  80 +
  81 + if ($res) {
  82 + $re = Db::name('recycleBin')->where('id', $id)->delete();
  83 + if ($re) {
  84 + $this->success("还原成功!");
  85 + }
  86 + }
  87 + }
  88 + }
  89 +
  90 + /**
  91 + * 回收站彻底删除
  92 + * @adminMenu(
  93 + * 'name' => '回收站彻底删除',
  94 + * 'parent' => 'index',
  95 + * 'display'=> false,
  96 + * 'hasView'=> false,
  97 + * 'order' => 10000,
  98 + * 'icon' => '',
  99 + * 'remark' => '回收站彻底删除',
  100 + * 'param' => ''
  101 + * )
  102 + */
  103 + public function delete()
  104 + {
  105 + $id = $this->request->param('id');
  106 + $result = Db::name('recycleBin')->where('id', $id)->find();
  107 + //删除资源
  108 + if ($result) {
  109 +
  110 + //页面没有单独的表.
  111 + if ($result['table_name'] === 'portal_post#page') {
  112 + $re = Db::name('portal_post')->where('id', $result['object_id'])->delete();
  113 + //消除路由
  114 + $routeModel = new RouteModel();
  115 + $routeModel->setRoute('', 'portal/Page/index', ['id' => $result['object_id']], 2, 5000);
  116 + $routeModel->getRoutes(true);
  117 + } else {
  118 + $re = Db::name($result['table_name'])->where('id', $result['object_id'])->delete();
  119 + }
  120 +
  121 + if ($re) {
  122 + $res = Db::name('recycleBin')->where('id', $id)->delete();
  123 + if ($result['table_name'] === 'portal_post') {
  124 + Db::name('portal_category_post')->where('post_id', $result['object_id'])->delete();
  125 + Db::name('portal_tag_post')->where('post_id', $result['object_id'])->delete();
  126 + }
  127 + if ($res) {
  128 + $this->success("删除成功!");
  129 + }
  130 +
  131 + }
  132 + }
  133 + }
  134 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: 老猫 <thinkcmf@126.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace app\admin\controller;
  12 +
  13 +use app\admin\model\RouteModel;
  14 +use cmf\controller\AdminBaseController;
  15 +use think\Db;
  16 +
  17 +class RouteController extends AdminBaseController
  18 +{
  19 +
  20 + /**
  21 + * 路由规则列表
  22 + * @adminMenu(
  23 + * 'name' => 'URL美化',
  24 + * 'parent' => 'admin/Setting/default',
  25 + * 'display'=> true,
  26 + * 'hasView'=> true,
  27 + * 'order' => 10000,
  28 + * 'icon' => '',
  29 + * 'remark' => 'URL规则管理',
  30 + * 'param' => ''
  31 + * )
  32 + */
  33 + public function index()
  34 + {
  35 + global $CMF_GV_routes;
  36 + $routeModel = new RouteModel();
  37 + $routes = Db::name('route')->order("list_order asc")->select();
  38 + $routeModel->getRoutes(true);
  39 + unset($CMF_GV_routes);
  40 + $this->assign("routes", $routes);
  41 + return $this->fetch();
  42 + }
  43 +
  44 + /**
  45 + * 添加路由规则
  46 + * @adminMenu(
  47 + * 'name' => '添加路由规则',
  48 + * 'parent' => 'index',
  49 + * 'display'=> false,
  50 + * 'hasView'=> true,
  51 + * 'order' => 10000,
  52 + * 'icon' => '',
  53 + * 'remark' => '添加路由规则',
  54 + * 'param' => ''
  55 + * )
  56 + */
  57 + public function add()
  58 + {
  59 + return $this->fetch();
  60 + }
  61 +
  62 + /**
  63 + * 添加路由规则提交
  64 + * @adminMenu(
  65 + * 'name' => '添加路由规则提交',
  66 + * 'parent' => 'index',
  67 + * 'display'=> false,
  68 + * 'hasView'=> false,
  69 + * 'order' => 10000,
  70 + * 'icon' => '',
  71 + * 'remark' => '添加路由规则提交',
  72 + * 'param' => ''
  73 + * )
  74 + */
  75 + public function addPost()
  76 + {
  77 + $data = $this->request->param();
  78 + $routeModel = new RouteModel();
  79 + $result = $this->validate($data, 'Route');
  80 + if ($result !== true) {
  81 + $this->error($result);
  82 + }
  83 + $routeModel->allowField(true)->save($data);
  84 +
  85 + $this->success("添加成功!", url("Route/index", ['id' => $routeModel->id]));
  86 + }
  87 +
  88 + /**
  89 + * 路由规则编辑
  90 + * @adminMenu(
  91 + * 'name' => '路由规则编辑',
  92 + * 'parent' => 'index',
  93 + * 'display'=> false,
  94 + * 'hasView'=> true,
  95 + * 'order' => 10000,
  96 + * 'icon' => '',
  97 + * 'remark' => '路由规则编辑',
  98 + * 'param' => ''
  99 + * )
  100 + */
  101 + public function edit()
  102 + {
  103 + $id = $this->request->param("id", 0, 'intval');
  104 + $route = Db::name('route')->where('id', $id)->find();
  105 + $this->assign($route);
  106 + return $this->fetch();
  107 + }
  108 +
  109 + /**
  110 + * 路由规则编辑提交
  111 + * @adminMenu(
  112 + * 'name' => '路由规则编辑提交',
  113 + * 'parent' => 'index',
  114 + * 'display'=> false,
  115 + * 'hasView'=> false,
  116 + * 'order' => 10000,
  117 + * 'icon' => '',
  118 + * 'remark' => '路由规则编辑提交',
  119 + * 'param' => ''
  120 + * )
  121 + */
  122 + public function editPost()
  123 + {
  124 + $data = $this->request->param();
  125 + $routeModel = new RouteModel();
  126 + $result = $this->validate($data, 'Route');
  127 + if ($result !== true) {
  128 + $this->error($result);
  129 + }
  130 + $routeModel->allowField(true)->isUpdate(true)->save($data);
  131 +
  132 + $this->success("保存成功!", url("Route/index"));
  133 + }
  134 +
  135 + /**
  136 + * 路由规则删除
  137 + * @adminMenu(
  138 + * 'name' => '路由规则删除',
  139 + * 'parent' => 'index',
  140 + * 'display'=> false,
  141 + * 'hasView'=> false,
  142 + * 'order' => 10000,
  143 + * 'icon' => '',
  144 + * 'remark' => '路由规则删除',
  145 + * 'param' => ''
  146 + * )
  147 + */
  148 + public function delete()
  149 + {
  150 + $id = $this->request->param('id', 0, 'intval');
  151 + RouteModel::destroy($id);
  152 +
  153 + $this->success("删除成功!");
  154 + }
  155 +
  156 + /**
  157 + * 路由规则禁用
  158 + * @adminMenu(
  159 + * 'name' => '路由规则禁用',
  160 + * 'parent' => 'index',
  161 + * 'display'=> false,
  162 + * 'hasView'=> false,
  163 + * 'order' => 10000,
  164 + * 'icon' => '',
  165 + * 'remark' => '路由规则禁用',
  166 + * 'param' => ''
  167 + * )
  168 + */
  169 + public function ban()
  170 + {
  171 + $id = $this->request->param("id", 0, 'intval');
  172 + $data = [];
  173 + $data['status'] = 0;
  174 + $data['id'] = $id;
  175 + $routeModel = new RouteModel();
  176 +
  177 + $routeModel->isUpdate(true)->save($data);
  178 + $this->success("禁用成功!");
  179 + }
  180 +
  181 + /**
  182 + * 路由规则启用
  183 + * @adminMenu(
  184 + * 'name' => '路由规则启用',
  185 + * 'parent' => 'index',
  186 + * 'display'=> false,
  187 + * 'hasView'=> false,
  188 + * 'order' => 10000,
  189 + * 'icon' => '',
  190 + * 'remark' => '路由规则启用',
  191 + * 'param' => ''
  192 + * )
  193 + */
  194 + public function open()
  195 + {
  196 + $id = $this->request->param("id", 0, 'intval');
  197 + $data = [];
  198 + $data['status'] = 1;
  199 + $data['id'] = $id;
  200 + $routeModel = new RouteModel();
  201 +
  202 + $routeModel->isUpdate(true)->save($data);
  203 + $this->success("启用成功!");
  204 + }
  205 +
  206 + /**
  207 + * 路由规则排序
  208 + * @adminMenu(
  209 + * 'name' => '路由规则排序',
  210 + * 'parent' => 'index',
  211 + * 'display'=> false,
  212 + * 'hasView'=> false,
  213 + * 'order' => 10000,
  214 + * 'icon' => '',
  215 + * 'remark' => '路由规则排序',
  216 + * 'param' => ''
  217 + * )
  218 + */
  219 + public function listOrder()
  220 + {
  221 + $routeModel = new RouteModel();
  222 + parent::listOrders($routeModel);
  223 + $this->success("排序更新成功!");
  224 + }
  225 +
  226 + /**
  227 + * 选择 URL
  228 + * @adminMenu(
  229 + * 'name' => '选择URL',
  230 + * 'parent' => 'index',
  231 + * 'display'=> false,
  232 + * 'hasView'=> true,
  233 + * 'order' => 10000,
  234 + * 'icon' => '',
  235 + * 'remark' => '选择URL',
  236 + * 'param' => ''
  237 + * )
  238 + */
  239 + public function select()
  240 + {
  241 + $routeModel = new RouteModel();
  242 + $urls = $routeModel->getAppUrls();
  243 +
  244 + $this->assign('urls', $urls);
  245 + return $this->fetch();
  246 + }
  247 +
  248 + function _suggest_url($action, $url)
  249 + {
  250 + $actionArr = explode('/', $action);
  251 +
  252 + $params = array_keys($url['vars']);
  253 +
  254 + $urlDepr1Params = [];
  255 +
  256 + $urlDepr2Params = [];
  257 +
  258 + if (!empty($params)) {
  259 +
  260 + foreach ($params as $param) {
  261 + if (empty($url['vars'][$param]['require'])) {
  262 + array_push($urlDepr1Params, "[:$param]");
  263 + } else {
  264 + array_push($urlDepr1Params, ":$param");
  265 + }
  266 +
  267 + array_push($urlDepr2Params, htmlspecialchars('<') . $param . htmlspecialchars('>'));
  268 + }
  269 +
  270 + }
  271 +
  272 + if ($actionArr[2] == 'index') {
  273 + $actionArr[1] = cmf_parse_name($actionArr[1]);
  274 + return empty($params) ? $actionArr[1] . '$' : ($actionArr[1] . '/' . implode('/', $urlDepr1Params) /*. '或' . $actionArr[1] . '-' . implode('-', $urlDepr2Params)*/);
  275 + } else {
  276 + $actionArr[2] = cmf_parse_name($actionArr[2]);
  277 + return empty($params) ? $actionArr[2] . '$' : ($actionArr[2] . '/' . implode('/', $urlDepr1Params) /*. '或' . $actionArr[2] . '-' . implode('-', $urlDepr2Params)*/);
  278 + }
  279 +
  280 + }
  281 +
  282 + function _url_vars($url)
  283 + {
  284 + if (!empty($url['vars'])) {
  285 + return implode(',', array_keys($url['vars']));
  286 + }
  287 +
  288 + return '';
  289 + }
  290 +
  291 +}