From 35fbc6229cfbe05cc5b857e98368fbb4aea85d00 Mon Sep 17 00:00:00 2001
From: Karson <karsonzhang@163.com>
Date: Sat, 24 Jun 2017 11:54:04 +0800
Subject: [PATCH] 新增日志自定义标题和内容 修复Install.php的权限检测 修复微信接口的部分BUG 修复微信登录的参数BUG 优化commonSearch插件默认值判断

---
 application/admin/command/Install/fastadmin.sql    |  0
 application/admin/controller/Index.php             |  1 +
 application/admin/controller/wechat/Autoreply.php  | 25 +++++++++++++++++++++++++
 application/admin/controller/wechat/Response.php   |  5 +++--
 application/admin/lang/zh-cn.php                   |  4 +---
 application/admin/lang/zh-cn/wechat/autoreply.php  |  7 ++++---
 application/admin/model/AdminLog.php               | 43 +++++++++++++++++++++++++++++++++----------
 application/admin/view/wechat/autoreply/add.html   |  6 +++---
 application/admin/view/wechat/autoreply/edit.html  | 34 ++++++++++++++--------------------
 application/admin/view/wechat/response/add.html    |  6 ------
 application/admin/view/wechat/response/edit.html   |  2 +-
 application/extra/site.php                         |  2 --
 application/index/view/layout/bootstrap.html       | 10 ++++++++--
 extend/fast/service/Wechat.php                     |  6 +++---
 extend/fast/third/Wechat.php                       |  4 ++--
 public/assets/js/backend/example/bootstraptable.js |  2 +-
 public/assets/js/backend/example/tabletemplate.js  |  2 +-
 public/assets/js/backend/wechat/response.js        | 12 +++++-------
 public/assets/js/bootstrap-table-commonsearch.js   | 20 +++++++++++++++-----
 public/assets/js/bootstrap-table-template.js       | 17 +++++++++++++++--
 public/install.php                                 | 32 ++++++++++++++++++++++++--------
 21 files changed, 159 insertions(+), 81 deletions(-)
 mode change 100644 => 100755 application/admin/command/Install/fastadmin.sql

diff --git a/application/admin/command/Install/fastadmin.sql b/application/admin/command/Install/fastadmin.sql
old mode 100644
new mode 100755
index 8678d57..8678d57
--- a/application/admin/command/Install/fastadmin.sql
+++ b/application/admin/command/Install/fastadmin.sql
diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php
index d6a6c0d..056d7a3 100644
--- a/application/admin/controller/Index.php
+++ b/application/admin/controller/Index.php
@@ -73,6 +73,7 @@ class Index extends Backend
                 $this->error($validate->getError(), $url, ['token' => $this->request->token()]);
                 return;
             }
+            \app\admin\model\AdminLog::setTitle(__('Login'));
             $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
             if ($result === true)
             {
diff --git a/application/admin/controller/wechat/Autoreply.php b/application/admin/controller/wechat/Autoreply.php
index fe1977c..0de2127 100644
--- a/application/admin/controller/wechat/Autoreply.php
+++ b/application/admin/controller/wechat/Autoreply.php
@@ -14,6 +14,7 @@ class Autoreply extends Backend
 {
 
     protected $model = null;
+    protected $noNeedRight = ['check_text_unique'];
 
     public function _initialize()
     {
@@ -46,4 +47,28 @@ class Autoreply extends Backend
         return $this->view->fetch();
     }
 
+    /**
+     * 判断文本是否唯一
+     * @internal
+     */
+    public function check_text_unique()
+    {
+        $row = $this->request->post("row/a");
+        $except = $this->request->post("except");
+        $text = isset($row['text']) ? $row['text'] : '';
+        if ($this->model->where('text', $text)->where(function($query) use($except) {
+                    if ($except)
+                    {
+                        $query->where('text', '<>', $except);
+                    }
+                })->count() == 0)
+        {
+            return json(['ok' => '']);
+        }
+        else
+        {
+            return json(['error' => __('Text already exists')]);
+        }
+    }
+
 }
diff --git a/application/admin/controller/wechat/Response.php b/application/admin/controller/wechat/Response.php
index 295fe96..d1bbb1a 100644
--- a/application/admin/controller/wechat/Response.php
+++ b/application/admin/controller/wechat/Response.php
@@ -14,6 +14,7 @@ class Response extends Backend
 {
 
     protected $model = null;
+    protected $searchFields = 'id,title';
 
     public function _initialize()
     {
@@ -38,7 +39,7 @@ class Response extends Backend
         {
             $this->code = -1;
             $params = $this->request->post("row/a");
-            $params['eventkey'] = $params['eventkey'] ? $params['eventkey'] : uniqid();
+            $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
             $params['content'] = json_encode($params['content']);
             $params['createtime'] = time();
             if ($params)
@@ -67,7 +68,7 @@ class Response extends Backend
         {
             $this->code = -1;
             $params = $this->request->post("row/a");
-            $params['eventkey'] = $params['eventkey'] ? $params['eventkey'] : uniqid();
+            $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid();
             $params['content'] = json_encode($params['content']);
             if ($params)
             {
diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php
index 76b1335..a55c770 100644
--- a/application/admin/lang/zh-cn.php
+++ b/application/admin/lang/zh-cn.php
@@ -1,9 +1,6 @@
 <?php
 
 return [
-    'Keep login'                                            => '保持会话',
-    'Sign in'                                               => '登入',
-    'Username'                                              => '用户名',
     'User id'                                               => '会员ID',
     'Username'                                              => '用户名',
     'Nickname'                                              => '昵称',
@@ -49,6 +46,7 @@ return [
     'None'                                                  => '无',
     'Home'                                                  => '主页',
     'Online'                                                => '在线',
+    'Login'                                                 => '登录',
     'Logout'                                                => '注销',
     'Profile'                                               => '个人资料',
     'Index'                                                 => '首页',
diff --git a/application/admin/lang/zh-cn/wechat/autoreply.php b/application/admin/lang/zh-cn/wechat/autoreply.php
index 883aed2..6ba7061 100644
--- a/application/admin/lang/zh-cn/wechat/autoreply.php
+++ b/application/admin/lang/zh-cn/wechat/autoreply.php
@@ -1,7 +1,8 @@
 <?php
 
 return [
-    'Text'      => '文本',
-    'Event key' => '响应标识',
-    'Remark' => '备注',
+    'Text'                => '文本',
+    'Event key'           => '响应标识',
+    'Remark'              => '备注',
+    'Text already exists' => '文本已经存在',
 ];
diff --git a/application/admin/model/AdminLog.php b/application/admin/model/AdminLog.php
index 8177a23..b4f3668 100644
--- a/application/admin/model/AdminLog.php
+++ b/application/admin/model/AdminLog.php
@@ -12,29 +12,52 @@ class AdminLog extends Model
     // 定义时间戳字段名
     protected $createTime = 'createtime';
     protected $updateTime = '';
+    //自定义日志标题
+    protected static $title = '';
+    //自定义日志内容
+    protected static $content = '';
+
+    public static function setTitle($title)
+    {
+        self::$title = $title;
+    }
+
+    public static function setContent($content)
+    {
+        self::$content = $content;
+    }
 
     public static function record($title = '')
     {
         $admin = \think\Session::get('admin');
         $admin_id = $admin ? $admin->id : 0;
         $username = $admin ? $admin->username : __('Unknown');
-        $content = request()->param();
-        foreach ($content as $k => $v)
+        $content = self::$content;
+        if (!$content)
         {
-            if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false)
+            $content = request()->param();
+            foreach ($content as $k => $v)
             {
-                unset($content[$k]);
+                if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false)
+                {
+                    unset($content[$k]);
+                }
             }
         }
-        $title = [];
-        $breadcrumb = \app\admin\library\Auth::instance()->getBreadcrumb();
-        foreach ($breadcrumb as $k => $v)
+        $title = self::$title;
+        if (!$title)
         {
-            $title[] = $v['title'];
+            $title = [];
+            $breadcrumb = \app\admin\library\Auth::instance()->getBreadcrumb();
+            foreach ($breadcrumb as $k => $v)
+            {
+                $title[] = $v['title'];
+            }
+            $title = implode(' ', $title);
         }
         self::create([
-            'title'     => implode(' ', $title),
-            'content'   => json_encode($content),
+            'title'     => $title,
+            'content'   => !is_scalar($content) ? json_encode($content) : $content,
             'url'       => request()->url(),
             'admin_id'  => $admin_id,
             'username'  => $username,
diff --git a/application/admin/view/wechat/autoreply/add.html b/application/admin/view/wechat/autoreply/add.html
index 2f92c82..118cd13 100644
--- a/application/admin/view/wechat/autoreply/add.html
+++ b/application/admin/view/wechat/autoreply/add.html
@@ -9,19 +9,19 @@
     <div class="form-group">
         <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[title]" value=""  id="c-title" class="form-control" required />
+            <input type="text" name="row[title]" value=""  id="c-title" class="form-control" data-rule="required" />
         </div>
     </div>
     <div class="form-group">
         <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[text]" value=""  id="c-text" class="form-control" required />
+            <input type="text" name="row[text]" value=""  id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique)" />
         </div>
     </div>
     <div class="form-group">
         <label for="c-content" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[eventkey]" id="c-eventkey" class="form-control" value="" required readonly />
+            <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="" data-rule="required" readonly />
             <div class="clickbox">
                 <span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a></span>
                 <span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
diff --git a/application/admin/view/wechat/autoreply/edit.html b/application/admin/view/wechat/autoreply/edit.html
index e7b4d55..cbc9485 100644
--- a/application/admin/view/wechat/autoreply/edit.html
+++ b/application/admin/view/wechat/autoreply/edit.html
@@ -1,4 +1,4 @@
-<link href="<?= $site['cdnurl'] ?>/assets/css/wechat/menu.css?v=<?= $site['version'] ?>" rel="stylesheet">
+<link href="{:$site['cdnurl'] ?>/assets/css/wechat/menu.css?v={$site.version}" rel="stylesheet">
 <style>
     .clickbox {margin:0;text-align: left;}
     .create-click {
@@ -8,41 +8,35 @@
 <form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
 
     <div class="form-group">
-        <label for="c-title" class="control-label col-xs-12 col-sm-2"><?= __('Title') ?>:</label>
+        <label for="c-title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[title]" value="<?= $row['title'] ?>"  id="c-title" class="form-control" required />
+            <input type="text" name="row[title]" value="{$row.title}"  id="c-title" class="form-control" data-rule="required" />
         </div>
     </div>
     <div class="form-group">
-        <label for="c-text" class="control-label col-xs-12 col-sm-2"><?= __('Text') ?>:</label>
+        <label for="c-text" class="control-label col-xs-12 col-sm-2">{:__('Text')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[text]" value="<?= $row['text'] ?>"  id="c-text" class="form-control" required />
+            <input type="text" name="row[text]" value="{$row.text}"  id="c-text" class="form-control" data-rule="required; remote(wechat/autoreply/check_text_unique, except={$row.text})" />
         </div>
     </div>
     <div class="form-group">
-        <label for="c-content" class="control-label col-xs-12 col-sm-2"><?= __('Content') ?>:</label>
+        <label for="c-content" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
         <div class="col-xs-12 col-sm-8">
             <div class="clickbox">
-                <input type="hidden" name="row[eventkey]" id="c-eventkey" value="<?= $row['eventkey'] ?>" />
-                <span class="create-click"><a href="<?= url('wechat.response/select') ?>" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a><div class="keytitle">资源名:<?= $response['title'] ?></div></span>
-                <span class="create-click"><a href="<?= url('wechat.response/add') ?>" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
+                <input type="hidden" name="row[eventkey]" id="c-eventkey" class="form-control" value="{$row.eventkey}" data-rule="required" readonly />
+                <span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a><div class="keytitle">资源名:{:$response['title'] ?></div></span>
+                <span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
             </div>
         </div>
     </div>
     <div class="form-group">
-        <label for="c-remark" class="control-label col-xs-12 col-sm-2"><?= __('Remark') ?>:</label>
+        <label for="c-remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type="text" name="row[remark]" value="<?= $row['remark'] ?>"  id="c-remark" class="form-control" />
+            <input type="text" name="row[remark]" value="{$row.remark}"  id="c-remark" class="form-control" />
         </div>
     </div>
     <div class="form-group">
-        <label for="c-createtime" class="control-label col-xs-12 col-sm-2"><?= __('Createtime') ?>:</label>
-        <div class="col-xs-12 col-sm-8">
-            <input type="datetime" name="row[createtime]" value="<?= date("Y-m-d H:i:s", $row['createtime']) ?>"  id="c-createtime" class="form-control datetimepicker" />
-        </div>
-    </div>
-    <div class="form-group">
-        <label class="control-label col-xs-12 col-sm-2"><?= __('Status') ?>:</label>
+        <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
         <div class="col-xs-12 col-sm-8">
             {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
         </div>
@@ -50,8 +44,8 @@
     <div class="form-group hide layer-footer">
         <label class="control-label col-xs-12 col-sm-2"></label>
         <div class="col-xs-12 col-sm-8">
-            <button type="submit" class="btn btn-success btn-embossed"><?= __('OK') ?></button>
-            <button type="reset" class="btn btn-default btn-embossed"><?= __('Reset') ?></button>
+            <button type="submit" class="btn btn-success btn-embossed">{:__('OK')}</button>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
         </div>
     </div>
 </form>
diff --git a/application/admin/view/wechat/response/add.html b/application/admin/view/wechat/response/add.html
index 91c81e7..b3a36b2 100644
--- a/application/admin/view/wechat/response/add.html
+++ b/application/admin/view/wechat/response/add.html
@@ -6,12 +6,6 @@
         </div>
     </div>
     <div class="form-group">
-        <label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
-        <div class="col-xs-12 col-sm-8">
-            <input type="text" class="form-control" id="eventkey" name="row[eventkey]" value="" pattern="[A-Za-z0-9_]{1,}" readonly />
-        </div>
-    </div>
-    <div class="form-group">
         <label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Memo')}:</label>
         <div class="col-xs-12 col-sm-8">
             <textarea class="form-control" id="remark" name="row[remark]"></textarea>
diff --git a/application/admin/view/wechat/response/edit.html b/application/admin/view/wechat/response/edit.html
index 32936d0..ceece66 100644
--- a/application/admin/view/wechat/response/edit.html
+++ b/application/admin/view/wechat/response/edit.html
@@ -9,7 +9,7 @@
     <div class="form-group">
         <label for="controller" class="control-label col-xs-12 col-sm-2">{:__('Event key')}:</label>
         <div class="col-xs-12 col-sm-8">
-            <input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" pattern="[A-Za-z0-9_]{1,}" data-rule="required" readonly />
+            <input type='text' class="form-control" id="eventkey" name="row[eventkey]" value="{$row.eventkey}" data-rule="required" readonly />
         </div>
     </div>
     <div class="form-group">
diff --git a/application/extra/site.php b/application/extra/site.php
index 3f39416..1f44948 100644
--- a/application/extra/site.php
+++ b/application/extra/site.php
@@ -28,6 +28,4 @@ return array (
     'user' => '会员配置',
     'example' => '示例分组',
   ),
-  'aaaa' => '',
-  'ffff' => 'key2',
 );
\ No newline at end of file
diff --git a/application/index/view/layout/bootstrap.html b/application/index/view/layout/bootstrap.html
index c5bfe42..71509bb 100644
--- a/application/index/view/layout/bootstrap.html
+++ b/application/index/view/layout/bootstrap.html
@@ -40,10 +40,16 @@
     <body>
         <div class="navbar navbar-default navbar-fixed-top">
             <div class="container">
-                <div class="navbar-header pull-left">
+                <div class="navbar-header">
+                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-main">
+                        <span class="sr-only">Toggle navigation</span>
+                        <span class="icon-bar"></span>
+                        <span class="icon-bar"></span>
+                        <span class="icon-bar"></span>
+                    </button>
                     <a href="{:url('/')}" class="navbar-brand">FastAdmin</a>
                 </div>
-                <div class="navbar-collapse collapse navbar-responsive-collapse">
+                <div class="collapse navbar-collapse" id="navbar-collapse-main">
                     <ul class="nav navbar-nav">
                         <li><a href="http://www.fastadmin.net">官网</a></li>
                         <li><a href="http://doc.fastadmin.net">文档</a></li>
diff --git a/extend/fast/service/Wechat.php b/extend/fast/service/Wechat.php
index e777dfe..8431293 100644
--- a/extend/fast/service/Wechat.php
+++ b/extend/fast/service/Wechat.php
@@ -141,13 +141,13 @@ class Wechat
                     $thirdinfo = UserThird::get(['platform' => 'wechat', 'openid' => $openid]);
                     if (!$thirdinfo)
                     {
-                        $response = '您还没有<a href="' . url('user/profile', 1) . '">绑定用户</a>还不能签到!';
+                        $response = '您还没有<a href="' . url('index/user/third', 'action=redirect&platform=wechat', true, true) . '">绑定用户</a>还不能签到!';
                     }
                     else
                     {
                         $user_id = $thirdinfo->user_id;
                         $usersign = new UserSignin;
-                        $signdata = $usersign->get([['user_id', '=', $user_id], ['createtime', '>=', Date::unixtime()]]);
+                        $signdata = $usersign->where('user_id', '=', $user_id)->where('createtime', '>=', Date::unixtime())->find();
                         if ($signdata)
                         {
                             $response = '今天已签到,请明天再来!';
@@ -156,7 +156,7 @@ class Wechat
                         {
                             $signdata = (array) json_decode(WechatConfig::value('signin'), TRUE);
 
-                            $lastdata = $usersign->where('user_id', $user_id)->order('id', 'desc')->limit(1)->get();
+                            $lastdata = $usersign->where('user_id', $user_id)->order('id', 'desc')->limit(1)->find();
                             $successions = $lastdata && $lastdata['createtime'] > Date::unixtime('day', -1) ? $lastdata['successions'] + 1 : 1;
                             $usersign->save(['user_id' => $thirdinfo['user_id'], 'successions' => $successions, 'createtime' => time()]);
                             $score = isset($signdata['s' . $successions]) ? $signdata['s' . $successions] : $signdata['sn'];
diff --git a/extend/fast/third/Wechat.php b/extend/fast/third/Wechat.php
index 6dd7f29..d616ddd 100644
--- a/extend/fast/third/Wechat.php
+++ b/extend/fast/third/Wechat.php
@@ -12,7 +12,7 @@ use think\Session;
 class Wechat
 {
 
-    const GET_AUTH_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/authorize";
+    const GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";
     const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
     const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
 
@@ -47,7 +47,7 @@ class Wechat
         $state = md5(uniqid(rand(), TRUE));
         Session::set('state', $state);
         $queryarr = array(
-            "app_id"        => $this->config['app_id'],
+            "appid"        => $this->config['app_id'],
             "redirect_uri"  => $this->config['callback'],
             "response_type" => "code",
             "scope"         => $this->config['scope'],
diff --git a/public/assets/js/backend/example/bootstraptable.js b/public/assets/js/backend/example/bootstraptable.js
index 5e696cd..cc30b37 100644
--- a/public/assets/js/backend/example/bootstraptable.js
+++ b/public/assets/js/backend/example/bootstraptable.js
@@ -30,7 +30,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         //模糊搜索
                         {field: 'title', title: __('Title'), operate: 'LIKE %...%', placeholder: '模糊搜索,*表示任意字符', style: 'width:200px'},
                         //通过Ajax渲染searchList,也可以使用JSON数据
-                        {field: 'url', title: __('Url'), align: 'left', searchList: $.getJSON('ajax/typeahead?search=a&field=row[user_id]'), formatter: Controller.api.formatter.url},
+                        {field: 'url', title: __('Url'), align: 'left', defaultValue:3, searchList: $.getJSON('ajax/typeahead?search=a&field=row[user_id]'), formatter: Controller.api.formatter.url},
                         //点击IP时同时执行搜索此IP,同时普通搜索使用下拉列表的形式
                         {field: 'ip', title: __('IP'), searchList: ['127.0.0.1', '127.0.0.2'], events: Controller.api.events.ip, formatter: Controller.api.formatter.ip},
                         //browser是一个不存在的字段
diff --git a/public/assets/js/backend/example/tabletemplate.js b/public/assets/js/backend/example/tabletemplate.js
index 621fa76..c3378d1 100644
--- a/public/assets/js/backend/example/tabletemplate.js
+++ b/public/assets/js/backend/example/tabletemplate.js
@@ -49,7 +49,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                 //可以控制是否默认显示搜索单表,false则隐藏,默认为false
                 searchFormVisible: false,
                 //分页大小
-                pageSize:12
+                pageSize: 12
             });
 
             // 为表格绑定事件
diff --git a/public/assets/js/backend/wechat/response.js b/public/assets/js/backend/wechat/response.js
index 887aedd..ab5ed77 100644
--- a/public/assets/js/backend/wechat/response.js
+++ b/public/assets/js/backend/wechat/response.js
@@ -26,8 +26,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'type', title: __('Type')},
                         {field: 'title', title: __('Resource title')},
                         {field: 'eventkey', title: __('Event key')},
-                        {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime},
-                        {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
+                        {field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate:false},
                         {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ]
@@ -57,8 +56,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'type', title: __('Type')},
                         {field: 'title', title: __('Title')},
                         {field: 'event', title: __('Event')},
-                        {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime},
-                        {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
+                        {field: 'status', title: __('Status'), formatter: Table.api.formatter.status, operate:false},
                         {field: 'operate', title: __('Operate'), events: {
                                 'click .btn-chooseone': function (e, value, row, index) {
                                     var callback = Backend.api.query('callback');
@@ -108,9 +106,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                             if (undefined != appConfig[i]['pattern'])
                                 pattern_str = 'pattern ="' + appConfig[i]['pattern'] + '" ';
                             if (appConfig[i]['type'] == 'textarea') {
-                                str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + appConfig[i]['caption'] + ':</label><div class="col-xs-12 col-sm-8"><textarea class="form-control" name="row[content][' + appConfig[i]['field'] + ']" ' + pattern_str + ' alt="' + alt + '"></textarea> </div> </div>';
+                                str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + appConfig[i]['caption'] + ':</label><div class="col-xs-12 col-sm-8"><textarea class="form-control" name="row[content][' + appConfig[i]['field'] + ']" ' + pattern_str + ' alt="' + alt + '" data-rule="required"></textarea> </div> </div>';
                             } else {
-                                str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + appConfig[i]['caption'] + ':</label><div class="col-xs-12 col-sm-8"><input class="form-control" name="row[content][' + appConfig[i]['field'] + ']" type="text" ' + pattern_str + ' alt="' + alt + '"> </div> </div>';
+                                str += '<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">' + appConfig[i]['caption'] + ':</label><div class="col-xs-12 col-sm-8"><input class="form-control" name="row[content][' + appConfig[i]['field'] + ']" type="text" ' + pattern_str + ' alt="' + alt + '" data-rule="required"> </div> </div>';
                             }
                         } else {
                             var options = appConfig[i]['options'];
@@ -157,7 +155,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 $(document).on('click', "input[name='row[type]']", function () {
                     var type = $(this).val();
                     if (type == 'text') {
-                        $("#expand").html('<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">文本内容:</label><div class="col-xs-12 col-sm-8"><textarea class="form-control" name="row[content][content]"></textarea> <a href="javascript:;" class="btn-insertlink">插入链接</a></div></div>');
+                        $("#expand").html('<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">文本内容:</label><div class="col-xs-12 col-sm-8"><textarea class="form-control" name="row[content][content]" data-rule="required"></textarea> <a href="javascript:;" class="btn-insertlink">插入链接</a></div></div>');
                         $("form.form-ajax").field("row[content][content]", datas.content);
                     } else if (type == 'app') {
                         $("#expand").html('<div class="form-group"><label for="content" class="control-label col-xs-12 col-sm-2">应用:</label><div class="col-xs-12 col-sm-8"><select class="form-control" name="row[content][app]" id="app">' + $("select[name=applist]").html() + '</select></div></div><div id="appfields"><div>');
diff --git a/public/assets/js/bootstrap-table-commonsearch.js b/public/assets/js/bootstrap-table-commonsearch.js
index 797f123..c6d597a 100644
--- a/public/assets/js/bootstrap-table-commonsearch.js
+++ b/public/assets/js/bootstrap-table-commonsearch.js
@@ -86,14 +86,24 @@
                         htmlForm.push(sprintf('<select class="form-control" name="%s" %s>%s</select>', vObjCol.field, style, sprintf('<option value="">%s</option>', that.options.formatCommonChoose())));
                         (function (vObjCol, that) {
                             $.when(vObjCol.searchList).done(function (ret) {
+                                
+                                var isArray = false;
                                 if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
-                                    var optionList = [];
+                                    var resultlist = {};
                                     $.each(ret.data.searchlist, function (key, value) {
-                                        var isSelect = value.id === vObjCol.defaultValue ? 'selected' : '';
-                                        optionList.push(sprintf("<option value='" + value.id + "' %s>" + value.name + "</option>", isSelect));
+                                        resultlist[value.id] = value.name;
                                     });
-                                    $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).append(optionList.join(''));
+                                } else if (ret.constructor === Array || ret.constructor === Object) {
+                                    var resultlist = ret;
+                                    isArray = ret.constructor === Array ? true : isArray;
                                 }
+                                console.log(resultlist);
+                                var optionList = [];
+                                $.each(resultlist, function (key, value) {
+                                    var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
+                                    optionList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
+                                });
+                                $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).append(optionList.join(''));
                             });
                         })(vObjCol, that);
                     } else if (typeof vObjCol.searchList == 'function') {
@@ -103,7 +113,7 @@
                         var searchList = [];
                         searchList.push(sprintf('<option value="">%s</option>', that.options.formatCommonChoose()));
                         $.each(vObjCol.searchList, function (key, value) {
-                            var isSelect = (isArray ? value : key) === vObjCol.defaultValue ? 'selected' : '';
+                            var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
                             searchList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
                         });
                         htmlForm.push(sprintf('<select class="form-control" name="%s" %s>%s</select>', vObjCol.field, style, searchList.join('')));
diff --git a/public/assets/js/bootstrap-table-template.js b/public/assets/js/bootstrap-table-template.js
index 98485d1..d446081 100644
--- a/public/assets/js/bootstrap-table-template.js
+++ b/public/assets/js/bootstrap-table-template.js
@@ -1,10 +1,23 @@
+/**
+ * 将BootstrapTable的行使用自定义的模板来渲染
+ * 
+ * @author: karson
+ * @version: v0.0.1
+ *
+ * @update 2017-06-24 <http://github.com/karsonzhang/fastadmin>
+ */
+
 !function ($) {
     'use strict';
 
     $.extend($.fn.bootstrapTable.defaults, {
+        //是否启用模板渲染
         templateView: false,
+        //数据格式化的模板ID或格式函数
         templateFormatter: "itemtpl",
+        //添加的父类的class
         templateParentClass: "row row-flex",
+        //向table添加的class
         templateTableClass: "table-template",
 
     });
@@ -30,14 +43,14 @@
             showFooter: !that.options.templateView ? $.fn.bootstrapTable.defaults.showFooter : false,
         });
         $(that.$el).toggleClass(that.options.templateTableClass, that.options.templateView);
-        
+
         _initBody.apply(this, Array.prototype.slice.apply(arguments));
 
         if (!that.options.templateView) {
             return;
         } else {
             //由于Bootstrap是基于Table的,添加一个父类容器
-            $("> *", that.$body).wrapAll($("<div />").addClass(that.options.templateParentClass));
+            $("> *:not(.no-records-found)", that.$body).wrapAll($("<div />").addClass(that.options.templateParentClass));
         }
     };
 
diff --git a/public/install.php b/public/install.php
index 857c682..5ebae0e 100644
--- a/public/install.php
+++ b/public/install.php
@@ -43,10 +43,6 @@ if (is_file($lockFile))
 {
     $errInfo = "当前已经安装{$sitename},如果需要重新安装,请手动移除application/admin/command/Install/install.lock文件";
 }
-else if (!is_writeable($lockFile))
-{
-    $errInfo = "当前权限不足,无法写入锁定文件application/admin/command/Install/install.lock";
-}
 else if (version_compare(PHP_VERSION, '5.5.0', '<'))
 {
     $errInfo = "当前版本(" . PHP_VERSION . ")过低,请使用PHP5.5以上版本";
@@ -119,7 +115,12 @@ if (!$errInfo && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD']
     }
     try
     {
-        $sql = file_get_contents(INSTALL_PATH . 'fastadmin.sql');
+        //检测能否读取安装文件
+        $sql = @file_get_contents(INSTALL_PATH . 'fastadmin.sql');
+        if (!$sql)
+        {
+            throw new Exception("无法读取application/admin/command/Install/fastadmin.sql文件,请检查是否有读权限");
+        }
         $pdo = new PDO("mysql:host={$mysqlHostname};port={$mysqlHostport}", $mysqlUsername, $mysqlPassword, array(
             PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
             PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
@@ -131,7 +132,7 @@ if (!$errInfo && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD']
 
         $pdo->exec($sql);
 
-        $config = file_get_contents($dbConfigFile);
+        $config = @file_get_contents($dbConfigFile);
         $callback = function($matches) use($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase) {
             $field = ucfirst($matches[1]);
             $replace = ${"mysql{$field}"};
@@ -142,14 +143,29 @@ if (!$errInfo && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD']
             return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}'{$replace}',";
         };
         $config = preg_replace_callback("/'(hostname|database|username|password|hostport)'(\s+)=>(\s+)'(.*)'\,/", $callback, $config);
-        file_put_contents($dbConfigFile, $config);
+        
+        //检测能否成功写入数据库配置
+        $result = @file_put_contents($dbConfigFile, $config);
+        if (!$result)
+        {
+            throw new Exception("无法写入数据库信息到application/database.php文件,请检查是否有写权限");
+        }
 
+        //检测能否成功写入lock文件
+        $result = @file_put_contents($lockFile, 1);
+        if (!$result)
+        {
+            throw new Exception("无法写入安装锁定到application/admin/command/Install/install.lock文件,请检查是否有写权限");
+        }
         $newSalt = substr(md5(uniqid(true)), 0, 6);
         $newPassword = md5(md5($adminPassword) . $newSalt);
         $pdo->query("UPDATE fa_admin SET username = '{$adminUsername}', email = '{$adminEmail}',password = '{$newPassword}', salt = '{$newSalt}' WHERE username = 'admin'");
-        file_put_contents($lockFile, 1);
         echo "success";
     }
+    catch (Exception $e)
+    {
+        $err = $e->getMessage();
+    }
     catch (PDOException $e)
     {
         $err = $e->getMessage();
--
libgit2 0.24.0