作者 Karson

优化安装脚本

优化SelectPage编辑时按顺序显示
优化分类、省市联动列表接口逻辑
@@ -20,14 +20,15 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。 @@ -20,14 +20,15 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
20 * 强大的插件扩展功能,在线安装卸载升级插件 20 * 强大的插件扩展功能,在线安装卸载升级插件
21 * 通用的会员模块和API模块 21 * 通用的会员模块和API模块
22 * 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证 22 * 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
23 -* 二级域名部署支持,同时域名支持绑定到插件 23 +* 二级域名部署支持,同时域名支持绑定到应用插件
24 * 多语言支持,服务端及客户端支持 24 * 多语言支持,服务端及客户端支持
25 * 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩 25 * 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩
26 -* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)[博客](https://www.fastadmin.net/store/blog.html)[知识付费问答](https://www.fastadmin.net/store/ask.html)[在线投票系统](https://www.fastadmin.net/store/vote.html)[商城系统](https://www.fastadmin.net/store/shopro.html)) 26 +* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能
  27 +* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)[博客](https://www.fastadmin.net/store/blog.html)[知识付费问答](https://www.fastadmin.net/store/ask.html)[在线投票系统](https://www.fastadmin.net/store/vote.html)[B2C商城](https://www.fastadmin.net/store/shopro.html)[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html))
27 * 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html) 28 * 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html)
28 -* 第三方小程序支持([预订小程序](https://www.fastadmin.net/store/ball.html)[问答小程序](https://www.fastadmin.net/store/questions.html)[活动报名小程序](https://www.fastadmin.net/store/huodong.html)[商城小程序](https://www.fastadmin.net/store/xshop.html)[博客小程序](https://www.fastadmin.net/store/blog.html)) 29 +* 第三方小程序支持([CMS小程序](https://www.fastadmin.net/store/cms.html)[预订小程序](https://www.fastadmin.net/store/ball.html)[问答小程序](https://www.fastadmin.net/store/ask.html)[点餐小程序](https://www.fastadmin.net/store/unidrink.html)[B2C小程序](https://www.fastadmin.net/store/shopro.html)[B2B2C小程序](https://www.fastadmin.net/store/wanlshop.html)[博客小程序](https://www.fastadmin.net/store/blog.html))
29 * 整合第三方短信接口(阿里云、腾讯云短信) 30 * 整合第三方短信接口(阿里云、腾讯云短信)
30 -* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能 31 +* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能,支持云储存分片上传
31 * 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器) 32 * 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器)
32 * 第三方登录(QQ、微信、微博)整合 33 * 第三方登录(QQ、微信、微博)整合
33 * 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付 34 * 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付
@@ -48,7 +49,7 @@ https://demo.fastadmin.net @@ -48,7 +49,7 @@ https://demo.fastadmin.net
48 提 示:演示站数据无法进行修改,请下载源码安装体验全部功能 49 提 示:演示站数据无法进行修改,请下载源码安装体验全部功能
49 50
50 ## 界面截图 51 ## 界面截图
51 -![控制台](https://gitee.com/uploads/images/2017/0411/113717_e99ff3e7_10933.png "控制台") 52 +![控制台](https://images.gitee.com/uploads/images/2020/0929/202947_8db2d281_10933.gif "控制台")
52 53
53 ## 问题反馈 54 ## 问题反馈
54 55
@@ -90,7 +90,10 @@ class Install extends Command @@ -90,7 +90,10 @@ class Install extends Command
90 $this->request = Request::instance(); 90 $this->request = Request::instance();
91 91
92 define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS); 92 define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
93 - Lang::load(INSTALL_PATH . $this->request->langset() . '.php'); 93 + $langSet = $this->request->langset();
  94 + if ($langSet === 'zh-cn') {
  95 + Lang::load(INSTALL_PATH . $langSet . '.php');
  96 + }
94 97
95 $installLockFile = INSTALL_PATH . "install.lock"; 98 $installLockFile = INSTALL_PATH . "install.lock";
96 99
@@ -196,23 +199,19 @@ class Install extends Command @@ -196,23 +199,19 @@ class Install extends Command
196 // 后台入口文件 199 // 后台入口文件
197 $adminFile = ROOT_PATH . 'public' . DS . 'admin.php'; 200 $adminFile = ROOT_PATH . 'public' . DS . 'admin.php';
198 201
199 - // 数据库配置文件  
200 - $dbConfigFile = APP_PATH . 'database.php';  
201 - $config = @file_get_contents($dbConfigFile); 202 + // 生成数据库Env配置文件
  203 + $envFile = ROOT_PATH . '.env.sample';
  204 + $envStr = @file_get_contents($envFile);
202 $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) { 205 $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) {
203 $field = "mysql" . ucfirst($matches[1]); 206 $field = "mysql" . ucfirst($matches[1]);
204 $replace = $$field; 207 $replace = $$field;
205 - if ($matches[1] == 'hostport' && $mysqlHostport == 3306) {  
206 - $replace = '';  
207 - }  
208 - return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),"; 208 + return "{$matches[1]} = {$replace}" . PHP_EOL;
209 }; 209 };
210 - $config = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $config); 210 + $envConf = preg_replace_callback('/(hostname|database|username|password|hostport|prefix)\s*=\s*(.*?)\n/', $callback, $envStr);
211 211
212 - // 检测能否成功写入数据库配置  
213 - $result = @file_put_contents($dbConfigFile, $config); 212 + $result = @file_put_contents(ROOT_PATH . '.env', $envConf);
214 if (!$result) { 213 if (!$result) {
215 - throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/database.php')); 214 + throw new Exception(__('The current permissions are insufficient to write the file %s', '.env'));
216 } 215 }
217 216
218 // 变更默认管理员密码 217 // 变更默认管理员密码
@@ -222,6 +221,12 @@ class Install extends Command @@ -222,6 +221,12 @@ class Install extends Command
222 $newPassword = md5(md5($adminPassword) . $newSalt); 221 $newPassword = md5(md5($adminPassword) . $newSalt);
223 $data = ['username' => $adminUsername, 'email' => $adminEmail, 'password' => $newPassword, 'salt' => $newSalt]; 222 $data = ['username' => $adminUsername, 'email' => $adminEmail, 'password' => $newPassword, 'salt' => $newSalt];
224 $instance->name('admin')->where('username', 'admin')->update($data); 223 $instance->name('admin')->where('username', 'admin')->update($data);
  224 +
  225 + // 变更前台默认用户的密码,随机生成
  226 + $newSalt = substr(md5(uniqid(true)), 0, 6);
  227 + $newPassword = md5(md5(Random::alnum(8)) . $newSalt);
  228 + $instance->name('user')->where('username', 'admin')->update(['password' => $newPassword, 'salt' => $newSalt]);
  229 +
225 // 修改后台入口 230 // 修改后台入口
226 $adminName = ''; 231 $adminName = '';
227 if (is_file($adminFile)) { 232 if (is_file($adminFile)) {
@@ -230,9 +235,9 @@ class Install extends Command @@ -230,9 +235,9 @@ class Install extends Command
230 } 235 }
231 236
232 //修改站点名称 237 //修改站点名称
233 - if ($siteName != __('My Website')) { 238 + if ($siteName != config('site.name')) {
234 $instance->name('config')->where('name', 'name')->update(['value' => $siteName]); 239 $instance->name('config')->where('name', 'name')->update(['value' => $siteName]);
235 - $configFile = APP_PATH . 'extra' . DS . 'site.php'; 240 + $configFile = CONF_PATH . 'extra' . DS . 'site.php';
236 $config = include $configFile; 241 $config = include $configFile;
237 $configList = $instance->name("config")->select(); 242 $configList = $instance->name("config")->select();
238 foreach ($configList as $k => $value) { 243 foreach ($configList as $k => $value) {
@@ -270,17 +275,15 @@ class Install extends Command @@ -270,17 +275,15 @@ class Install extends Command
270 'public' . DS . 'assets' . DS . 'libs' 275 'public' . DS . 'assets' . DS . 'libs'
271 ]; 276 ];
272 277
273 - //数据库配置文件  
274 - $dbConfigFile = APP_PATH . 'database.php';  
275 -  
276 - if (version_compare(PHP_VERSION, '5.5.0', '<')) {  
277 - throw new Exception(__("The current version %s is too low, please use PHP 5.5 or higher", PHP_VERSION)); 278 + if (version_compare(PHP_VERSION, '7.0.0', '<')) {
  279 + throw new Exception(__("The current version %s is too low, please use PHP 7.0 or higher", PHP_VERSION));
278 } 280 }
279 if (!extension_loaded("PDO")) { 281 if (!extension_loaded("PDO")) {
280 throw new Exception(__("PDO is not currently installed and cannot be installed")); 282 throw new Exception(__("PDO is not currently installed and cannot be installed"));
281 } 283 }
282 - if (!is_really_writable($dbConfigFile)) {  
283 - throw new Exception(__('The current permissions are insufficient to write the configuration file application/database.php')); 284 + $envConfFile = ROOT_PATH . '.env';
  285 + if (is_file($envConfFile) && !is_really_writable($envConfFile)) {
  286 + throw new Exception(__('The current permissions are insufficient to write the file %s', '.env'));
284 } 287 }
285 foreach ($checkDirs as $k => $v) { 288 foreach ($checkDirs as $k => $v) {
286 if (!is_dir(ROOT_PATH . $v)) { 289 if (!is_dir(ROOT_PATH . $v)) {
@@ -8,10 +8,10 @@ return [ @@ -8,10 +8,10 @@ return [
8 'Mysql Password' => 'MySQL 密码', 8 'Mysql Password' => 'MySQL 密码',
9 'Mysql Prefix' => 'MySQL 数据表前缀', 9 'Mysql Prefix' => 'MySQL 数据表前缀',
10 'Mysql Hostport' => 'MySQL 端口号', 10 'Mysql Hostport' => 'MySQL 端口号',
11 - 'Admin Username' => '管理者用户名',  
12 - 'Admin Email' => '管理者Email',  
13 - 'Admin Password' => '管理者密码',  
14 - 'Repeat Password' => '重复密码', 11 + 'Admin Username' => '管理员用户名',
  12 + 'Admin Email' => '管理员Email',
  13 + 'Admin Password' => '管理员密码',
  14 + 'Repeat Password' => '重复管理员密码',
15 'Website' => '网站名称', 15 'Website' => '网站名称',
16 'My Website' => '我的网站', 16 'My Website' => '我的网站',
17 'Install now' => '点击安装', 17 'Install now' => '点击安装',
@@ -26,9 +26,9 @@ return [ @@ -26,9 +26,9 @@ return [
26 'Please input correct password' => '密码长度必须在6-16位之间,不能包含空格', 26 'Please input correct password' => '密码长度必须在6-16位之间,不能包含空格',
27 'The two passwords you entered did not match' => '两次输入的密码不一致', 27 'The two passwords you entered did not match' => '两次输入的密码不一致',
28 'Please input correct website' => '网站名称输入不正确', 28 'Please input correct website' => '网站名称输入不正确',
29 - 'The current version %s is too low, please use PHP 5.5 or higher' => '当前版本%s过低,请使用PHP5.5以上版本', 29 + 'The current version %s is too low, please use PHP 7.0 or higher' => '当前版本%s过低,请使用PHP7.0以上版本',
30 'PDO is not currently installed and cannot be installed' => '当前未开启PDO,无法进行安装', 30 'PDO is not currently installed and cannot be installed' => '当前未开启PDO,无法进行安装',
31 'The current permissions are insufficient to write the file %s' => '当前权限不足,无法写入文件%s', 31 'The current permissions are insufficient to write the file %s' => '当前权限不足,无法写入文件%s',
32 'Please go to the official website to download the full package or resource package and try to install' => '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装', 32 'Please go to the official website to download the full package or resource package and try to install' => '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装',
33 'The system has been installed. If you need to reinstall, please remove %s first' => '当前已经安装成功,如果需要重新安装,请手动移除%s文件', 33 'The system has been installed. If you need to reinstall, please remove %s first' => '当前已经安装成功,如果需要重新安装,请手动移除%s文件',
34 -];  
  34 +];
@@ -29,7 +29,7 @@ class Ajax extends Backend @@ -29,7 +29,7 @@ class Ajax extends Backend
29 parent::_initialize(); 29 parent::_initialize();
30 30
31 //设置过滤方法 31 //设置过滤方法
32 - $this->request->filter(['strip_tags', 'htmlspecialchars']); 32 + $this->request->filter(['trim', 'strip_tags', 'htmlspecialchars']);
33 } 33 }
34 34
35 /** 35 /**
@@ -138,8 +138,8 @@ class Ajax extends Backend @@ -138,8 +138,8 @@ class Ajax extends Backend
138 $orderway = $orderway == 'asc' ? 'ASC' : 'DESC'; 138 $orderway = $orderway == 'asc' ? 'ASC' : 'DESC';
139 $sour = $weighdata = []; 139 $sour = $weighdata = [];
140 $ids = explode(',', $ids); 140 $ids = explode(',', $ids);
141 - $prikey = $pk ? $pk : (Db::name($table)->getPk() ?: 'id');  
142 - $pid = $this->request->post("pid"); 141 + $prikey = $pk && preg_match("/^[a-z0-9\-_]+$/i", $pk) ? $pk : (Db::name($table)->getPk() ?: 'id');
  142 + $pid = $this->request->post("pid", "");
143 //限制更新的字段 143 //限制更新的字段
144 $field = in_array($field, ['weigh']) ? $field : 'weigh'; 144 $field = in_array($field, ['weigh']) ? $field : 'weigh';
145 145
@@ -217,20 +217,20 @@ class Ajax extends Backend @@ -217,20 +217,20 @@ class Ajax extends Backend
217 */ 217 */
218 public function category() 218 public function category()
219 { 219 {
220 - $type = $this->request->get('type');  
221 - $pid = $this->request->get('pid'); 220 + $type = $this->request->get('type', '');
  221 + $pid = $this->request->get('pid', '');
222 $where = ['status' => 'normal']; 222 $where = ['status' => 'normal'];
223 - $categorylist = null; 223 + $categorylist = null;
224 if ($pid || $pid === '0') { 224 if ($pid || $pid === '0') {
225 $where['pid'] = $pid; 225 $where['pid'] = $pid;
226 } 226 }
227 if ($type) { 227 if ($type) {
228 $where['type'] = $type; 228 $where['type'] = $type;
229 } 229 }
230 - 230 +
231 $categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select(); 231 $categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select();
232 232
233 - $this->success('', null, $categorylist); 233 + $this->success('', '', $categorylist);
234 } 234 }
235 235
236 /** 236 /**
@@ -241,27 +241,23 @@ class Ajax extends Backend @@ -241,27 +241,23 @@ class Ajax extends Backend
241 $params = $this->request->get("row/a"); 241 $params = $this->request->get("row/a");
242 if (!empty($params)) { 242 if (!empty($params)) {
243 $province = isset($params['province']) ? $params['province'] : ''; 243 $province = isset($params['province']) ? $params['province'] : '';
244 - $city = isset($params['city']) ? $params['city'] : null; 244 + $city = isset($params['city']) ? $params['city'] : '';
245 } else { 245 } else {
246 - $province = $this->request->get('province');  
247 - $city = $this->request->get('city'); 246 + $province = $this->request->get('province', '');
  247 + $city = $this->request->get('city', '');
248 } 248 }
249 $where = ['pid' => 0, 'level' => 1]; 249 $where = ['pid' => 0, 'level' => 1];
250 $provincelist = null; 250 $provincelist = null;
251 if ($province !== '') { 251 if ($province !== '') {
252 - if ($province) {  
253 - $where['pid'] = $province;  
254 - $where['level'] = 2;  
255 - } 252 + $where['pid'] = $province;
  253 + $where['level'] = 2;
256 if ($city !== '') { 254 if ($city !== '') {
257 - if ($city) {  
258 - $where['pid'] = $city;  
259 - $where['level'] = 3;  
260 - }  
261 - $provincelist = Db::name('area')->where($where)->field('id as value,name')->select(); 255 + $where['pid'] = $city;
  256 + $where['level'] = 3;
262 } 257 }
263 } 258 }
264 - $this->success('', null, $provincelist); 259 + $provincelist = Db::name('area')->where($where)->field('id as value,name')->select();
  260 + $this->success('', '', $provincelist);
265 } 261 }
266 262
267 /** 263 /**
@@ -189,7 +189,7 @@ class Config extends Backend @@ -189,7 +189,7 @@ class Config extends Backend
189 $config[$value['name']] = $value['value']; 189 $config[$value['name']] = $value['value'];
190 } 190 }
191 file_put_contents( 191 file_put_contents(
192 - APP_PATH . 'extra' . DS . 'site.php', 192 + CONF_PATH . 'extra' . DS . 'site.php',
193 '<?php' . "\n\nreturn " . var_export_short($config) . ";\n" 193 '<?php' . "\n\nreturn " . var_export_short($config) . ";\n"
194 ); 194 );
195 } 195 }
@@ -112,7 +112,6 @@ @@ -112,7 +112,6 @@
112 overflow: hidden; 112 overflow: hidden;
113 text-overflow: ellipsis; 113 text-overflow: ellipsis;
114 display: inline-block; 114 display: inline-block;
115 - margin-right: 10px;  
116 } 115 }
117 116
118 .stat .value { 117 .stat .value {
@@ -535,25 +535,22 @@ class Backend extends Controller @@ -535,25 +535,22 @@ class Backend extends Controller
535 } 535 }
536 536
537 $fields = is_array($this->selectpageFields) ? $this->selectpageFields : ($this->selectpageFields && $this->selectpageFields != '*' ? explode(',', $this->selectpageFields) : []); 537 $fields = is_array($this->selectpageFields) ? $this->selectpageFields : ($this->selectpageFields && $this->selectpageFields != '*' ? explode(',', $this->selectpageFields) : []);
538 - 538 +
539 //如果有primaryvalue,说明当前是初始化传值,按照选择顺序排序 539 //如果有primaryvalue,说明当前是初始化传值,按照选择顺序排序
540 - if ($primaryvalue !== null) { 540 + if ($primaryvalue !== null && preg_match("/^[a-z0-9_\-]+$/i", $primarykey)) {
541 $primaryvalue = array_unique(is_array($primaryvalue) ? $primaryvalue : explode(',', $primaryvalue)); 541 $primaryvalue = array_unique(is_array($primaryvalue) ? $primaryvalue : explode(',', $primaryvalue));
542 - $primaryvalue = implode(',', array_map([$this->model->getConnection(), 'quote'], $primaryvalue));  
543 -  
544 - $datalist = $this->model->where($where)  
545 - ->orderRaw("FIELD(`{$primarykey}`, {$primaryvalue})")  
546 - ->page($page, $pagesize)  
547 - ->field($this->selectpageFields)  
548 - ->select(); 542 + $primaryvalue = implode(',', $primaryvalue);
  543 +
  544 + $this->model->orderRaw("FIELD(`{$primarykey}`, {$primaryvalue})");
549 } else { 545 } else {
550 - $datalist = $this->model->where($where)  
551 - ->order($order)  
552 - ->page($page, $pagesize)  
553 - ->field($this->selectpageFields)  
554 - ->select(); 546 + $this->model->order($order);
555 } 547 }
556 548
  549 + $datalist = $this->model->where($where)
  550 + ->page($page, $pagesize)
  551 + ->field($this->selectpageFields)
  552 + ->select();
  553 +
557 foreach ($datalist as $index => $item) { 554 foreach ($datalist as $index => $item) {
558 unset($item['password'], $item['salt']); 555 unset($item['password'], $item['salt']);
559 if ($this->selectpageFields == '*') { 556 if ($this->selectpageFields == '*') {
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 <p style="color:#adb9e0;line-height:30px;">网站(Website)是指在因特网上根据一定的规则,使用HTML(标准通用标记语言)等工具制作的用于展示特定内容相关网页的集合。简单地说,网站是一种沟通工具,人们可以通过网站来发布自己想要公开的资讯,或者利用网站来提供相关的网络服务。</p> 57 <p style="color:#adb9e0;line-height:30px;">网站(Website)是指在因特网上根据一定的规则,使用HTML(标准通用标记语言)等工具制作的用于展示特定内容相关网页的集合。简单地说,网站是一种沟通工具,人们可以通过网站来发布自己想要公开的资讯,或者利用网站来提供相关的网络服务。</p>
58 58
59 <div> 59 <div>
60 - <a href="{:url('index/user/index')}" class="btn bg-primary btn-xl btn-round-lg">会员中心</a> 60 + <a href="{:url('index/user/index')}" class="btn bg-primary btn-xl btn-round-lg">{:__('Member center')}</a>
61 </div> 61 </div>
62 </div> 62 </div>
63 </div> 63 </div>
@@ -26,7 +26,8 @@ @@ -26,7 +26,8 @@
26 "nelexa/zip": "^3.3", 26 "nelexa/zip": "^3.3",
27 "symfony/var-exporter": "^4.4.13", 27 "symfony/var-exporter": "^4.4.13",
28 "ext-json": "*", 28 "ext-json": "*",
29 - "ext-curl": "*" 29 + "ext-curl": "*",
  30 + "ext-pdo": "*"
30 }, 31 },
31 "config": { 32 "config": {
32 "preferred-install": "dist" 33 "preferred-install": "dist"