diff --git a/connect.py b/connect.py
index 8558ed745..05d84dd8f 100644
--- a/connect.py
+++ b/connect.py
@@ -19,10 +19,11 @@ import struct, fcntl, signal, socket, select
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
if django.get_version() != '1.6':
django.setup()
-from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object, mkdir
-from jumpserver.api import logger, Log, TtyLog
+from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object, mkdir, get_asset_info, get_role
+from jumpserver.api import logger, Log, TtyLog, get_role_key
+from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm
from jumpserver.settings import LOG_DIR
-
+from jperm.ansible_api import Command
login_user = get_object(User, username=getpass.getuser())
VIM_FLAG = False
@@ -73,15 +74,15 @@ class Tty(object):
A virtual tty class
一个虚拟终端类,实现连接ssh和记录日志,基类
"""
- def __init__(self, username, asset_name):
- self.username = username
- self.asset_name = asset_name
+ def __init__(self, user, asset, role):
+ self.username = user.username
+ self.asset_name = asset.hostname
self.ip = None
self.port = 22
self.channel = None
- #self.asset = get_object(Asset, name=asset_name)
- #self.user = get_object(User, username=username)
- self.role = None
+ self.asset = asset
+ self.user = user
+ self.role = role
self.ssh = None
self.connect_info = None
self.login_type = 'ssh'
@@ -287,7 +288,10 @@ class Tty(object):
# 2. get 映射用户
# 3. get 映射用户的账号,密码或者key
# self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''}
- self.connect_info = {'user': 'a', 'asset': 'b', 'ip': '127.0.0.1', 'port': 22, 'role_name': 'root', 'role_pass': 'redhat', 'role_key': ''}
+ asset_info = get_asset_info(self.asset)
+ self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'),
+ 'port': int(asset_info.get('port')), 'role_name': self.role.name,
+ 'role_pass': self.role.password, 'role_key': self.role.key_path}
return self.connect_info
def get_connection(self):
@@ -301,18 +305,24 @@ class Tty(object):
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
- if connect_info.get('role_pass'):
- ssh.connect(connect_info.get('ip'),
- port=connect_info.get('port'),
- username=connect_info.get('role_name'),
- password=connect_info.get('role_pass'),
- look_for_keys=False)
- else:
- ssh.connect(connect_info.get('ip'),
- port=connect_info.get('port'),
- username=connect_info.get('role_name'),
- key_filename=connect_info.get('role_key'),
- look_for_keys=False)
+ role_key = get_role_key(self.user, self.role)
+ if role_key and os.path.isfile(role_key):
+ try:
+ ssh.connect(connect_info.get('ip'),
+ port=connect_info.get('port'),
+ username=connect_info.get('role_name'),
+ key_filename=role_key,
+ look_for_keys=False)
+ self.ssh = ssh
+ return ssh
+ except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
+ pass
+
+ ssh.connect(connect_info.get('ip'),
+ port=connect_info.get('port'),
+ username=connect_info.get('role_name'),
+ password=connect_info.get('role_pass'),
+ look_for_keys=False)
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
raise ServerError('认证失败 Authentication Error.')
@@ -458,28 +468,12 @@ class SshTty(Tty):
channel.close()
ssh.close()
- def execute(self, cmd):
- """
- execute cmd on the asset
- 执行命令
- """
- pass
-
-
-def print_user_asset_group_info(user):
- asset_groups = AssetGroup.objects.all()
- for asset_group in asset_groups:
- if asset_group.comment:
- print '[%-2s] %-10s %s' % (asset_group.id, asset_group.name, asset_group.comment)
- else:
- print '[%-2s] %-10s' % (asset_group.id, asset_group.name)
- print
-
class Nav(object):
def __init__(self, user):
self.user = user
self.search_result = {}
+ self.user_perm = {}
@staticmethod
def print_nav():
@@ -510,7 +504,9 @@ class Nav(object):
def search(self, str_r=''):
gid_pattern = re.compile(r'^g\d+$')
- user_asset_all = list(Asset.objects.all())
+ if not self.user_perm:
+ self.user_perm = get_group_user_perm(self.user)
+ user_asset_all = self.user_perm.get('asset').keys()
user_asset_search = []
if str_r:
if gid_pattern.match(str_r):
@@ -523,13 +519,15 @@ class Nav(object):
user_asset_search = user_asset_all
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
-
- print '\033[32m[%-3s] %-15s %-15s %-5s %-5s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment')
+ print '\033[32m[%-3s] %-15s %-15s %-5s %-10s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment')
for index, asset in self.search_result.items():
+ asset_info = get_asset_info(asset)
+ role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
if asset.comment:
- print '[%-3s] %-15s %-15s %-5s %-5s %s' % (index, 'asset_name'+str(index), asset.ip, asset.port, 'role', asset.comment)
+ print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
+ role, asset.comment)
else:
- print '[%-3s] %-15s %-15s %-5s %-5s' % (index, 'asset_name'+str(index), asset.ip, asset.port, 'role')
+ print '[%-3s] %-15s %-15s %-5s %-10s' % (index, asset.hostname, asset.ip, asset_info.get('port'), role)
print
@staticmethod
@@ -544,6 +542,50 @@ class Nav(object):
print '[%-3s] %-15s' % (asset_group.id, asset_group.name)
print
+ def exec_cmd(self):
+ self.search()
+ while True:
+ print "请输入主机名、IP或ansile支持的pattern, q退出"
+ try:
+ pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip()
+ if pattern == 'q':
+ break
+ else:
+ if not self.user_perm:
+ self.user_perm = get_group_user_perm(self.user)
+ res = gen_resource(self.user, perm=self.user_perm)
+ cmd = Command(res)
+ logger.debug(res)
+ for inv in cmd.inventory.get_hosts(pattern=pattern):
+ print inv.name
+ confirm_host = raw_input("\033[1;32mIs that [y/n]>:\033[0m ").strip()
+ if confirm_host == 'y':
+ while True:
+ print "请输入执行的命令, 按q退出"
+ command = raw_input("\033[1;32mCmds>:\033[0m ").strip()
+ if command == 'q':
+ break
+ result = cmd.run(module_name='shell', command=command, pattern=pattern)
+ for k, v in result.items():
+ if k == 'ok':
+ for host, output in v.items():
+ color_print("%s => %s" % (host, 'Ok'), 'green')
+ print output
+ print
+ else:
+ for host, output in v.items():
+ color_print("%s => %s" % (host, k), 'red')
+ color_print(output, 'red')
+ print
+ print "=" * 20
+ print
+ else:
+ continue
+
+ except EOFError:
+ print
+ break
+
def main():
"""
@@ -575,14 +617,35 @@ def main():
nav.print_asset_group()
continue
elif option in ['E', 'e']:
- # exec_cmd_servers(login_name)
- pass
+ nav.exec_cmd()
+ continue
elif option in ['Q', 'q', 'exit']:
sys.exit()
else:
try:
asset = nav.search_result[int(option)]
- ssh_tty = SshTty('a', 'b')
+ roles = get_role(login_user, asset)
+ if len(roles) > 1:
+ role_check = dict(zip(range(len(roles)), roles))
+ print role_check
+ for index, role in role_check.items():
+ print "[%s] %s" % (index, role.name)
+ print "输入角色ID, q退出"
+ try:
+ role_index = raw_input("\033[1;32mID>:\033[0m ").strip()
+ if role_index == 'q':
+ continue
+ else:
+ role = role_check[int(role_index)]
+ except IndexError:
+ color_print('请输入正确ID', 'red')
+ continue
+ elif len(roles) == 1:
+ role = roles[0]
+ else:
+ color_print('没有映射用户', 'red')
+ continue
+ ssh_tty = SshTty(login_user, asset, role)
ssh_tty.connect()
except (KeyError, ValueError):
color_print('请输入正确ID', 'red')
diff --git a/jasset/asset_api.py b/jasset/asset_api.py
index 8103db23b..06c91a616 100644
--- a/jasset/asset_api.py
+++ b/jasset/asset_api.py
@@ -238,7 +238,6 @@ def db_asset_alert(asset, username, alert_dic):
alert_list = []
asset_tuple_dic = {'status': ASSET_STATUS, 'env': ASSET_ENV, 'asset_type': ASSET_TYPE}
for field, value in alert_dic.iteritems():
- print field
field_name = Asset._meta.get_field_by_name(field)[0].verbose_name
if field == 'idc':
old = IDC.objects.filter(id=value[0]) if value[0] else u''
@@ -386,9 +385,9 @@ def excel_to_db(excel_file):
if row:
ip, port, hostname, use_default_auth, username, password, group = row
use_default_auth = 1 if use_default_auth == u'默认' else 0
- if get_object(Asset, ip=ip):
+ if get_object(Asset, hostname=hostname):
continue
- if ip and port:
+ if hostname:
asset = Asset(ip=ip,
port=port,
hostname=hostname,
diff --git a/jasset/forms.py b/jasset/forms.py
index f9af4d499..03eca04ed 100644
--- a/jasset/forms.py
+++ b/jasset/forms.py
@@ -28,5 +28,10 @@ class IdcForm(forms.ModelForm):
class Meta:
model = IDC
fields = ['name', "bandwidth", "operator", 'linkman', 'phone', 'address', 'network', 'comment']
+ widgets = {
+ 'name': forms.TextInput(attrs={'placeholder': 'Name'}),
+ 'network': forms.Textarea(
+ attrs={'placeholder': '192.168.1.0/24\n192.168.2.0/24'})
+ }
diff --git a/jasset/models.py b/jasset/models.py
index f1e5b46fc..159d72254 100644
--- a/jasset/models.py
+++ b/jasset/models.py
@@ -42,7 +42,7 @@ class IDC(models.Model):
address = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"机房地址")
network = models.TextField(blank=True, null=True, verbose_name=u"IP地址段")
date_added = models.DateField(auto_now=True, null=True)
- operator = models.IntegerField(blank=True, null=True, verbose_name=u"运营商")
+ operator = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"运营商")
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"备注")
def __unicode__(self):
@@ -57,17 +57,17 @@ class Asset(models.Model):
"""
asset modle
"""
- ip = models.GenericIPAddressField(unique=True, verbose_name=u"主机IP")
+ ip = models.GenericIPAddressField(blank=True, null=True, verbose_name=u"主机IP")
other_ip = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"其他IP")
- hostname = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"主机名")
- port = models.IntegerField(verbose_name=u"端口号")
+ hostname = models.CharField(unique=True, max_length=128, verbose_name=u"主机名")
+ port = models.IntegerField(blank=True, null=True, verbose_name=u"端口号")
group = models.ManyToManyField(AssetGroup, blank=True, verbose_name=u"所属主机组")
username = models.CharField(max_length=16, blank=True, null=True, verbose_name=u"管理用户名")
password = models.CharField(max_length=64, blank=True, null=True, verbose_name=u"密码")
use_default_auth = models.BooleanField(default=True, verbose_name=u"使用默认管理账号")
idc = models.ForeignKey(IDC, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=u'机房')
mac = models.CharField(max_length=20, blank=True, null=True, verbose_name=u"MAC地址")
- remote_ip = models.CharField(max_length=16, blank=True, null=True, verbose_name=u'远控卡')
+ remote_ip = models.CharField(max_length=16, blank=True, null=True, verbose_name=u'远控卡IP')
brand = models.CharField(max_length=64, blank=True, null=True, verbose_name=u'硬件厂商型号')
cpu = models.CharField(max_length=64, blank=True, null=True, verbose_name=u'CPU')
memory = models.CharField(max_length=128, blank=True, null=True, verbose_name=u'内存')
diff --git a/jasset/urls.py b/jasset/urls.py
index c9ec8c462..10d6dafc7 100644
--- a/jasset/urls.py
+++ b/jasset/urls.py
@@ -17,13 +17,11 @@ urlpatterns = patterns('',
url(r'^group_list/$', group_list),
url(r'^group_edit/$', group_edit),
url(r'^group_list/$', group_list),
- url(r'^group_detail/$', group_detail),
# url(r'^group_del_host/$', group_del_host),
url(r'^asset_edit_batch/$', asset_edit_batch),
# url(r'^host_edit_common/batch/$', host_edit_common_batch),
url(r'^idc_add/$', idc_add),
url(r'^idc_list/$', idc_list),
- url(r'^idc_detail/$', idc_detail),
url(r'^idc_edit/$', idc_edit),
url(r'^idc_del/$', idc_del),
url(r'^upload/$', asset_upload),
diff --git a/jasset/views.py b/jasset/views.py
index 71448f22a..31282137e 100644
--- a/jasset/views.py
+++ b/jasset/views.py
@@ -26,13 +26,13 @@ def group_add(request):
try:
if not name:
- error = u'组名不能为空'
- raise ServerError(error)
+ emg = u'组名不能为空'
+ raise ServerError(emg)
asset_group_test = get_object(AssetGroup, name=name)
if asset_group_test:
- error = u"该组名 %s 已存在" % name
- raise ServerError(error)
+ emg = u"该组名 %s 已存在" % name
+ raise ServerError(emg)
except ServerError:
pass
@@ -87,21 +87,6 @@ def group_edit(request):
return my_render('jasset/group_edit.html', locals(), request)
-@require_role('admin')
-def group_detail(request):
- """
- Group detail view
- 主机组详情
- """
- header_title, path1, path2 = u'主机组详情', u'资产管理', u'主机组详情'
- group_id = request.GET.get('id', '')
- group = get_object(AssetGroup, id=group_id)
- asset_all = Asset.objects.filter(group=group).order_by('ip')
-
- contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(asset_all, request)
- return my_render('jasset/group_detail.html', locals(), request)
-
-
@require_role('admin')
def group_list(request):
"""
@@ -147,13 +132,13 @@ def asset_add(request):
af = AssetForm()
if request.method == 'POST':
af_post = AssetForm(request.POST)
- print af_post
ip = request.POST.get('ip', '')
+ hostname = request.POST.get('hostname', '')
is_active = True if request.POST.get('is_active') == '1' else False
use_default_auth = request.POST.get('use_default_auth', '')
try:
- if Asset.objects.filter(ip=str(ip)):
- error = u'该IP %s 已存在!' % ip
+ if Asset.objects.filter(hostname=str(hostname)):
+ error = u'该主机名 %s 已存在!' % hostname
raise ServerError(error)
except ServerError:
@@ -165,13 +150,15 @@ def asset_add(request):
password = request.POST.get('password', '')
password_encode = password
asset_save.password = password_encode
+ if not ip:
+ asset_save.ip = hostname
asset_save.is_active = True if is_active else False
asset_save.save()
af_post.save_m2m()
- msg = u'主机 %s 添加成功' % ip
+ msg = u'主机 %s 添加成功' % hostname
else:
- esg = u'主机 %s 添加失败' % ip
+ esg = u'主机 %s 添加失败' % hostname
return my_render('jasset/asset_add.html', locals(), request)
@@ -215,17 +202,21 @@ def asset_edit(request):
asset_id = request.GET.get('id', '')
username = request.session.get('username', 'admin')
asset = get_object(Asset, id=asset_id)
- asset_old = copy_model_instance(asset)
+ if asset:
+ password_old = asset.password
+ # asset_old = copy_model_instance(asset)
af = AssetForm(instance=asset)
if request.method == 'POST':
af_post = AssetForm(request.POST, instance=asset)
ip = request.POST.get('ip', '')
- use_default_auth = request.POST.get('use_default_auth')
+ hostname = request.POST.get('hostname', '')
+ password = request.POST.get('password', '')
+ use_default_auth = request.POST.get('use_default_auth', '')
try:
- asset_test = get_object(Asset, ip=ip)
+ asset_test = get_object(Asset, hostname=hostname)
if asset_test and asset_id != unicode(asset_test.id):
- error = u'该IP %s 已存在!' % ip
+ error = u'该主机名 %s 已存在!' % hostname
raise ServerError(error)
except ServerError:
pass
@@ -235,6 +226,10 @@ def asset_edit(request):
if use_default_auth:
af_save.username = ''
af_save.password = ''
+ else:
+ if password_old != password:
+ password_encode = CRYPTOR.encrypt(password)
+ af_save.password = password_encode
af_save.save()
af_post.save_m2m()
# asset_new = get_object(Asset, id=asset_id)
@@ -266,8 +261,19 @@ def asset_list(request):
status = request.GET.get('status', '')
keyword = request.GET.get('keyword', '')
export = request.GET.get("export", False)
+ group_id = request.GET.get("group_id", '')
+ idc_id = request.GET.get("idc_id", '')
+ if group_id:
+ group = get_object(AssetGroup, id=group_id)
+ if group:
+ asset_find = Asset.objects.filter(group=group)
+ elif idc_id:
+ idc = get_object(IDC, id=idc_id)
+ if idc:
+ asset_find = Asset.objects.filter(idc=idc)
+ else:
+ asset_find = Asset.objects.all()
- asset_find = Asset.objects.all()
if idc_name:
asset_find = asset_find.filter(idc__name__contains=idc_name)
@@ -305,7 +311,82 @@ def asset_list(request):
@require_role('admin')
def asset_edit_batch(request):
af = AssetForm()
+ name = request.session.get('username', 'admin')
asset_group_all = AssetGroup.objects.all()
+
+ if request.method == 'POST':
+ env = request.POST.get('env', '')
+ idc_id = request.POST.get('idc', '')
+ port = request.POST.get('port', '')
+ use_default_auth = request.POST.get('use_default_auth', '')
+ username = request.POST.get('username', '')
+ password = request.POST.get('password', '')
+ group = request.POST.getlist('group', [])
+ cabinet = request.POST.get('cabinet', '')
+ comment = request.POST.get('comment', '')
+ asset_id_all = unicode(request.GET.get('asset_id_all', ''))
+ asset_id_all = asset_id_all.split(',')
+ for asset_id in asset_id_all:
+ alert_list = []
+ asset = get_object(Asset, id=asset_id)
+ if asset:
+ if env:
+ if asset.env != env:
+ asset.env = env
+ alert_list.append([u'运行环境', asset.env, env])
+ if idc_id:
+ idc = get_object(IDC, id=idc_id)
+ name_old = asset.idc.name if asset.idc else u''
+ if idc and idc.name != name_old:
+ asset.idc = idc
+ alert_list.append([u'机房', name_old, idc.name])
+ if port:
+ if unicode(asset.port) != port:
+ asset.port = port
+ alert_list.append([u'端口号', asset.port, port])
+
+ if use_default_auth:
+ if use_default_auth == 'default':
+ asset.use_default_auth = 1
+ asset.username = ''
+ asset.password = ''
+ alert_list.append([u'使用默认管理账号', asset.use_default_auth, u'默认'])
+ elif use_default_auth == 'user_passwd':
+ asset.use_default_auth = 0
+ asset.username = username
+ password_encode = CRYPTOR.encrypt(password)
+ asset.password = password_encode
+ alert_list.append([u'使用默认管理账号', asset.use_default_auth, username])
+ if group:
+ group_new, group_old, group_new_name, group_old_name = [], asset.group.all(), [], []
+ for group_id in group:
+ g = get_object(AssetGroup, id=group_id)
+ if g:
+ group_new.append(g)
+ if not set(group_new) < set(group_old):
+ group_instance = list(set(group_new) | set(group_old))
+ for g in group_instance:
+ group_new_name.append(g.name)
+ for g in group_old:
+ group_old_name.append(g.name)
+ asset.group = group_instance
+ alert_list.append([u'主机组', ','.join(group_old_name), ','.join(group_new_name)])
+ if cabinet:
+ if asset.cabinet != cabinet:
+ asset.cabinet = cabinet
+ alert_list.append([u'机柜号', asset.cabinet, cabinet])
+ if comment:
+ if asset.comment != comment:
+ asset.comment = comment
+ alert_list.append([u'备注', asset.comment, comment])
+ asset.save()
+
+ if alert_list:
+ username = unicode(name) + ' - ' + u'批量'
+ print alert_list
+ AssetRecord.objects.create(asset=asset, username=username, content=alert_list)
+ return HttpResponse('ok')
+
return my_render('jasset/asset_edit_batch.html', locals(), request)
@@ -437,29 +518,17 @@ def idc_edit(request):
return my_render('jasset/idc_edit.html', locals(), request)
-@require_role('admin')
-def idc_detail(request):
- """
- IDC detail view
- """
- header_title, path1, path2 = u'IDC详情', u'资产管理', u'IDC详情'
- idc_id = request.GET.get('id', '')
- idc = get_object(IDC, id=idc_id)
- posts = Asset.objects.filter(idc=idc).order_by('ip')
- contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
-
- return my_render('jasset/idc_detail.html', locals(), request)
-
-
@require_role('admin')
def idc_del(request):
"""
IDC delete view
"""
- uuid = request.GET.get('id', '')
- idc = get_object(IDC, id=uuid)
- if idc:
- idc.delete()
+ idc_ids = request.GET.get('id', '')
+ idc_id_list = idc_ids.split(',')
+
+ for idc_id in idc_id_list:
+ IDC.objects.filter(id=idc_id).delete()
+
return HttpResponseRedirect('/jasset/idc_list/')
diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py
index 4e66da246..1b0e5ce38 100644
--- a/jperm/ansible_api.py
+++ b/jperm/ansible_api.py
@@ -79,14 +79,17 @@ class MyInventory(object):
for host in hosts:
# set connection variables
hostname = host.get("hostname")
+ hostip = host.get('ip', hostname)
hostport = host.get("port")
username = host.get("username")
password = host.get("password")
+ ssh_key = host.get("ssh_key")
my_host = Host(name=hostname, port=hostport)
- my_host.set_variable('ansible_ssh_host', hostname)
+ my_host.set_variable('ansible_ssh_host', hostip)
my_host.set_variable('ansible_ssh_port', hostport)
my_host.set_variable('ansible_ssh_user', username)
my_host.set_variable('ansible_ssh_pass', password)
+ my_host.set_variable('ansible_ssh_private_key_file', ssh_key)
# set other variables
for key, value in host.iteritems():
if key not in ["hostname", "port", "username", "password"]:
@@ -107,43 +110,72 @@ class MyInventory(object):
self.add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars"))
+class MyRunner(MyInventory):
+ """
+ This is a General object for parallel execute modules.
+ """
+ def __init__(self, *args, **kwargs):
+ super(MyRunner, self).__init__(*args, **kwargs)
+ self.results = {}
+
+ def run(self, module_name, module_args='', timeout=10, forks=10, pattern='',
+ sudo=False, sudo_user='root', sudo_pass=''):
+ """
+ run module from andible ad-hoc.
+ module_name: ansible module_name
+ module_args: ansible module args
+ """
+ hoc = Runner(module_name=module_name,
+ module_args=module_args,
+ timeout=timeout,
+ inventory=self.inventory,
+ pattern=pattern,
+ forks=forks,
+ become=sudo,
+ become_method='sudo',
+ become_user=sudo_user,
+ become_pass=sudo_pass
+ )
+ self.results = hoc.run()
+ return self.results
+
+
class Command(MyInventory):
"""
this is a command object for parallel execute command.
"""
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
- self.results = ''
+ self.results = {}
- def run(self, command, module_name="command", timeout=10, forks=10, group='default_group', pattern='*'):
+ def run(self, command, module_name="command", timeout=10, forks=10, pattern='*'):
"""
run command from andible ad-hoc.
command : 必须是一个需要执行的命令字符串, 比如
'uname -a'
"""
+ data = {}
if module_name not in ["raw", "command", "shell"]:
raise CommandValueError("module_name",
- "module_name must be of the 'raw, command, shell'")
+ "module_name must be of the 'raw, command, shell'")
hoc = Runner(module_name=module_name,
module_args=command,
timeout=timeout,
inventory=self.inventory,
- subset=group,
pattern=pattern,
forks=forks,
)
self.results = hoc.run()
if self.stdout:
- return {"ok": self.stdout}
- else:
- msg = []
- if self.stderr:
- msg.append(self.stderr)
- if self.dark:
- msg.append(self.dark)
- return {"failed": msg}
+ data['ok'] = self.stdout
+ if self.stderr:
+ data['err'] = self.stderr
+ if self.dark:
+ data['dark'] = self.dark
+
+ return data
@property
def raw_results(self):
@@ -174,7 +206,7 @@ class Command(MyInventory):
result = {}
all = self.results.get("contacted")
for key, value in all.iteritems():
- result[key] = value.get("stdout")
+ result[key] = value.get("stdout")
return result
@property
@@ -185,7 +217,8 @@ class Command(MyInventory):
result = {}
all = self.results.get("contacted")
for key, value in all.iteritems():
- result[key] = {
+ if value.get("stderr") or value.get("warnings"):
+ result[key] = {
"stderr": value.get("stderr"),
"warnings": value.get("warnings"),}
return result
@@ -371,7 +404,7 @@ class Tasks(Command):
result[key] = {
"all_ip": setup.get("ansible_all_ipv4_addresses"),
- "hostname" : setup.get("ansible_hostname" ),
+ "hostname" : setup.get("ansible_hostname"),
"default_ip": setup.get("ansible_default_ipv4").get("address"),
"default_mac": setup.get("ansible_default_ipv4").get("macaddress"),
"product_name": setup.get("ansible_product_name"),
@@ -388,8 +421,6 @@ class Tasks(Command):
return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result}
-
-
class CustomAggregateStats(callbacks.AggregateStats):
"""
Holds stats about per-host activity during playbook runs.
@@ -408,7 +439,6 @@ class CustomAggregateStats(callbacks.AggregateStats):
self.results.append(runner_results)
-
def summarize(self, host):
"""
Return information about a particular host
diff --git a/jperm/models.py b/jperm/models.py
index dc8643b67..0a01f7593 100644
--- a/jperm/models.py
+++ b/jperm/models.py
@@ -5,20 +5,6 @@ from jasset.models import Asset, AssetGroup
from juser.models import User, UserGroup
-class PermLog(models.Model):
- datetime = models.DateTimeField(auto_now_add=True)
- action = models.CharField(max_length=100, null=True, blank=True, default='')
- results = models.CharField(max_length=1000, null=True, blank=True, default='')
- is_success = models.BooleanField(default=False)
- is_finish = models.BooleanField(default=False)
-
-
-class SysUser(models.Model):
- username = models.CharField(max_length=100)
- password = models.CharField(max_length=100)
- comment = models.CharField(max_length=100, null=True, blank=True, default='')
-
-
class PermRole(models.Model):
name = models.CharField(max_length=100, unique=True)
comment = models.CharField(max_length=100, null=True, blank=True, default='')
@@ -41,4 +27,4 @@ class PermRule(models.Model):
role = models.ManyToManyField(PermRole, related_name='perm_rule')
def __unicode__(self):
- return self.name
\ No newline at end of file
+ return self.name
diff --git a/jperm/perm_api.py b/jperm/perm_api.py
index 1b363f547..856c13005 100644
--- a/jperm/perm_api.py
+++ b/jperm/perm_api.py
@@ -1,15 +1,163 @@
# coding: utf-8
-
+from django.db.models.query import QuerySet
from jumpserver.api import *
import uuid
import re
-from jumpserver.tasks import playbook_run
from jumpserver.models import Setting
-from jperm.models import PermLog
-
from jperm.models import PermRole
+from jperm.models import PermRule
+
+
+def get_group_user_perm(ob):
+ """
+ ob为用户或用户组
+ 获取用户、用户组授权的资产、资产组
+ return:
+ {’asset_group': {
+ asset_group1: {'asset': [], 'role': [role1, role2], 'rule': [rule1, rule2]},
+ asset_group2: {'asset: [], 'role': [role1, role2], 'rule': [rule1, rule2]},
+ }
+ 'asset':{
+ asset1: {'role': [role1, role2], 'rule': [rule1, rule2]},
+ asset2: {'role': [role1, role2], 'rule': [rule1, rule2]},
+ }
+ ]},
+ 'rule':[rule1, rule2,]
+ }
+ """
+ perm = {}
+ if isinstance(ob, User):
+ rule_all = PermRule.objects.filter(user=ob)
+ elif isinstance(ob, UserGroup):
+ rule_all = PermRule.objects.filter(user_group=ob)
+ else:
+ rule_all = []
+
+ perm['rule'] = rule_all
+ perm_asset_group = perm['asset_group'] = {}
+ perm_asset = perm['asset'] = {}
+ for rule in rule_all:
+ asset_groups = rule.asset_group.all()
+ assets = rule.asset.all()
+
+ # 获取一个规则用户授权的资产
+ for asset in assets:
+ if perm_asset.get(asset):
+ perm_asset[asset].get('role', set()).update(set(rule.role.all()))
+ perm_asset[asset].get('rule', set()).add(rule)
+ else:
+ perm_asset[asset] = {'role': set(rule.role.all()), 'rule': set([rule])}
+
+ # 获取一个规则用户授权的资产组
+ for asset_group in asset_groups:
+ asset_group_assets = asset_group.asset_set.all()
+ if perm_asset_group.get(asset_group):
+ perm_asset_group[asset_group].get('role', set()).update(set(rule.role.all()))
+ perm_asset_group[asset_group].get('rule', set()).add(rule)
+ else:
+ perm_asset_group[asset_group] = {'role': set(rule.role.all()), 'rule': set([rule]),
+ 'asset': asset_group_assets}
+
+ # 将资产组中的资产添加到资产授权中
+ for asset in asset_group_assets:
+ if perm_asset.get(asset):
+ perm_asset[asset].get('role', set()).update(perm_asset_group[asset_group].get('role', set()))
+ perm_asset[asset].get('rule', set()).update(perm_asset_group[asset_group].get('rule', set()))
+ else:
+ perm_asset[asset] = {'role': perm_asset_group[asset_group].get('role', set()),
+ 'rule': perm_asset_group[asset_group].get('rule', set())}
+ return perm
+
+
+def get_group_asset_perm(ob):
+ """
+ ob为资产或资产组
+ 获取资产,资产组授权的用户,用户组
+ return:
+ {’user_group': {
+ user_group1: {'user': [], 'role': [role1, role2], 'rule': [rule1, rule2]},
+ user_group2: {'user: [], 'role': [role1, role2], 'rule': [rule1, rule2]},
+ }
+ 'user':{
+ user1: {'role': [role1, role2], 'rule': [rule1, rule2]},
+ user2: {'role': [role1, role2], 'rule': [rule1, rule2]},
+ }
+ ]},
+ 'rule':[rule1, rule2,]
+ }
+ """
+ perm = {}
+ if isinstance(ob, Asset):
+ rule_all = PermRule.objects.filter(asset=ob)
+ elif isinstance(ob, AssetGroup):
+ rule_all = PermRule.objects.filter(asset_group=ob)
+ else:
+ rule_all = []
+
+ perm['rule'] = rule_all
+ perm_user_group = perm['user_group'] = {}
+ perm_user = perm['user'] = {}
+ for rule in rule_all:
+ user_groups = rule.user_group.all()
+ users = rule.user.all()
+
+ # 获取一个规则资产的用户
+ for user in users:
+ if perm_user.get(user):
+ perm_user[user].get('role', set()).update(set(rule.role.all()))
+ perm_user[user].get('rule', set()).add(rule)
+ else:
+ perm_user[user] = {'role': set(rule.role.all()), 'rule': set([rule])}
+
+ # 获取一个规则资产授权的用户组
+ for user_group in user_groups:
+ user_group_users = user_group.user_set.all()
+ if perm_user_group.get(user_group):
+ perm_user_group[user_group].get('role', set()).update(set(rule.role.all()))
+ perm_user_group[user_group].get('rule', set()).add(rule)
+ else:
+ perm_user_group[user_group] = {'role': set(rule.role.all()), 'rule': set([rule]),
+ 'user': user_group_users}
+
+ # 将用户组中的资产添加到用户授权中
+ for user in user_group_users:
+ if perm_user.get(user):
+ perm_user[user].get('role', set()).update(perm_user_group[user_group].get('role', set()))
+ perm_user[user].get('rule', set()).update(perm_user_group[user_group].get('rule', set()))
+ else:
+ perm_user[user] = {'role': perm_user_group[user_group].get('role', set()),
+ 'rule': perm_user_group[user_group].get('rule', set())}
+ return perm
+
+
+def gen_resource(ob, perm=None):
+ """
+ ob为用户或资产列表或资产queryset
+ 生成MyInventory需要的 resource文件
+ """
+ res = []
+ if isinstance(ob, User):
+ if not perm:
+ perm = get_group_user_perm(ob)
+
+ for asset, asset_info in perm.get('asset').items():
+ asset_info = get_asset_info(asset)
+ info = {'hostname': asset.hostname, 'ip': asset.ip, 'port': asset_info.get('port', 22)}
+ try:
+ role = sorted(list(perm.get('asset').get(asset).get('role')))[0]
+ except IndexError:
+ continue
+ info['username'] = role.name
+ info['password'] = role.password
+ info['ssh_key'] = get_role_key(ob, role)
+ res.append(info)
+ elif isinstance(ob, (list, QuerySet)):
+ for asset in ob:
+ info = get_asset_info(asset)
+ res.append(info)
+ return res
def get_object_list(model, id_list):
@@ -22,267 +170,6 @@ def get_object_list(model, id_list):
return object_list
-def get_rand_file_path(base_dir=os.path.join(BASE_DIR, 'tmp')):
- """获取随机文件路径"""
- filename = uuid.uuid1().hex
- return os.path.join(base_dir, filename)
-
-
-def get_inventory(host_group):
- """生成资产表库存清单"""
- path = get_rand_file_path()
- f = open(path, 'w')
- for group, host_list in host_group.items():
- f.write('[%s]\n' % group)
- for ip in host_list:
- asset = get_object(Asset, ip=ip)
- if asset.use_default:
- f.write('%s\n' % ip)
- else:
- f.write('%s ansible_ssh_port=%s ansible_ssh_user=%s ansible_ssh_pass=%s\n' %
- (ip, asset.port, asset.username, CRYPTOR.decrypt(asset.password)))
- f.close()
- return path
-
-
-def get_playbook(template, var):
- """根据playbook模板,生成playbook"""
- str_playbook = open(template).read()
- for k, v in var.items():
- str_playbook = re.sub(r'%s' % k, v, str_playbook) # 正则来替换传入的字符
- path = get_rand_file_path()
- f = open(path, 'w')
- f.write(str_playbook)
- return path
-
-
-def perm_user_api(perm_info):
- """
- 用户授权api,通过调用ansible API完成用户新建等,传入参数必须如下,列表中可以是对象,也可以是用户名和ip
- perm_info = {'del': {'users': [],
- 'assets': [],
- },
- 'new': {'users': [],
- 'assets': []}}
- """
- log = PermLog(action=perm_info.get('action', ''))
- try:
- new_users = perm_info.get('new', {}).get('users', [])
- new_assets = perm_info.get('new', {}).get('assets', [])
- del_users = perm_info.get('del', {}).get('users', [])
- del_assets = perm_info.get('del', {}).get('assets', [])
- print new_users, new_assets
- except IndexError:
- raise ServerError("Error: function perm_user_api传入参数错误")
-
- try:
- new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)]
- del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)]
- new_username = [user.username for user in new_users]
- del_username = [user.username for user in del_users]
- except IndexError:
- raise ServerError("Error: function perm_user_api传入参数类型错误")
-
- host_group = {'new': new_ip, 'del': del_ip}
- inventory = get_inventory(host_group)
-
- the_new_users = ','.join(new_username)
- the_del_users = ','.join(del_username)
-
- playbook = get_playbook(os.path.join(BASE_DIR, 'keys/../playbook', 'user_perm.yaml'),
- {'the_new_group': 'new', 'the_del_group': 'del',
- 'the_new_users': the_new_users, 'the_del_users': the_del_users,
- 'KEY_DIR': os.path.join(SSH_KEY_DIR, 'sysuser')})
-
- print playbook, inventory
-
- settings = get_object(Setting, name='default')
- results = playbook_run(inventory, playbook, settings)
- if not results.get('failures', 1) and not results.get('unreachable', ''):
- is_success = True
- else:
- is_success = False
-
- log.results = results
- log.is_finish = True
- log.is_success = is_success
- log.save()
- return results
-
-
-def user_group_permed(user_group):
- assets = user_group.asset.all()
- asset_groups = user_group.asset_group.all()
-
- for asset_group in asset_groups:
- assets.extend(asset_group.asset.all())
-
- return {'assets': assets, 'asset_groups': asset_groups}
-
-
-def user_permed(user):
- asset_groups = []
- assets = []
- user_groups = user.group.all()
- asset_groups.extend(user.asset_group.all())
- assets.extend(user.asset.all())
-
- for user_group in user_groups:
- asset_groups.extend(user_group_permed(user_group).get('assets', []))
- assets.extend((user_group_permed(user_group).get('asset_groups', [])))
-
- return {'assets': assets, 'asset_groups': asset_groups}
-
-
-def _public_perm_api(info):
- """
- 公用的用户,用户组,主机,主机组编辑修改新建调用的api,用来完成授权
- info like that:
- {
- 'type': 'new_user',
- 'user': 'a',
- 'group': ['A', 'B']
- }
-
- {
- 'type': 'edit_user',
- 'user': 'a',
- 'group': {'new': ['A'], 'del': []}
- }
-
- {
- 'type': 'del_user',
- 'user': ['a', 'b']
- }
-
- {
- 'type': 'edit_user_group',
- 'group': 'A',
- 'user': {'del': ['a', 'b'], 'new': ['c', 'd']}
- }
-
- {
- 'type': 'del_user_group',
- 'group': ['A']
- }
-
- {
- 'type': 'new_asset',
- 'asset': 'a',
- 'group': ['A', 'B']
- }
-
- {
- 'type': 'edit_asset',
- 'asset': 'a',
- 'group': {
- 'del': ['A', ['B'],
- 'new': ['C', ['D']]
- }
- }
-
- {
- 'type': 'del_asset',
- 'asset': ['a', 'b']
- }
-
- {
- 'type': 'edit_asset_group',
- 'group': 'A',
- 'asset': {'new': ['a', 'b'], 'del': ['c', 'd']}
- }
-
- {
- 'type': 'del_asset_group',
- 'group': ['A', 'B']
- }
- """
-
- if info.get('type') == 'new_user':
- new_assets = []
- user = info.get('user')
- user_groups = info.get('group')
- for user_group in user_groups:
- new_assets.extend(user_group_permed(user_group).get('assets', []))
-
- perm_info = {
- 'action': 'new user: ' + user.name,
- 'new': {'users': [user], 'assets': new_assets}
- }
- elif info.get('type') == 'edit_user':
- new_assets = []
- del_assets = []
- user = info.get('user')
- new_group = info.get('group').get('new')
- del_group = info.get('group').get('del')
-
- for user_group in new_group:
- new_assets.extend(user_group_permed(user_group).get('assets', []))
-
- for user_group in del_group:
- del_assets.extend((user_group_permed(user_group).get('assets', [])))
-
- perm_info = {
- 'action': 'edit user: ' + user.name,
- 'del': {'users': [user], 'assets': del_assets},
- 'new': {'users': [user], 'assets': new_assets}
- }
-
- elif info.get('type') == 'del_user':
- user = info.get('user')
- del_assets = user_permed(user).get('assets', [])
- perm_info = {
- 'action': 'del user: ' + user.name, 'del': {'users': [user], 'assets': del_assets},
- }
-
- elif info.get('type') == 'edit_user_group':
- user_group = info.get('group')
- new_users = info.get('user').get('new')
- del_users = info.get('user').get('del')
- assets = user_group_permed(user_group).get('assets', [])
-
- perm_info = {
- 'action': 'edit user group: ' + user_group.name,
- 'new': {'users': new_users, 'assets': assets},
- 'del': {'users': del_users, 'assets': assets}
- }
-
- elif info.get('type') == 'del_user_group':
- user_group = info.get('group', [])
- del_users = user_group.user_set.all()
- assets = user_group_permed(user_group).get('assets', [])
-
- perm_info = {
- 'action': "del user group: " + user_group.name, 'del': {'users': del_users, 'assets': assets}
- }
- else:
- return
-
- try:
- results = perm_user_api(perm_info) # 通过API授权或回收
- except ServerError, e:
- return e
- else:
- return results
-
-
-def push_user(user, asset_groups_id):
- assets = []
- if not user:
- return {'error': '没有该用户'}
- for group_id in asset_groups_id:
- asset_group = get_object(AssetGroup, id=group_id)
- if asset_group:
- assets.extend(asset_group.asset_set.all())
- perm_info = {
- 'action': 'Push user:' + user.username,
- 'new': {'users': [user], 'assets': assets}
- }
-
- results = perm_user_api(perm_info)
- return results
-
-
def get_role_info(role_id, type="all"):
"""
获取role对应的一些信息
diff --git a/jperm/urls.py b/jperm/urls.py
index aa80f7f75..fd40dc1cd 100644
--- a/jperm/urls.py
+++ b/jperm/urls.py
@@ -13,11 +13,4 @@ urlpatterns = patterns('jperm.views',
(r'^role/perm_role_detail/$', perm_role_detail),
(r'^role/perm_role_edit/$', perm_role_edit),
(r'^role/perm_role_push/$', perm_role_push),
-
-
- (r'^log/$', log),
- (r'^sys_user_add/$', sys_user_add),
- (r'^perm_user_list/$', sys_user_list),
- (r'^sys_user_del/$', sys_user_del),
- (r'^sys_user_edit/$', sys_user_edit),
)
diff --git a/jperm/utils.py b/jperm/utils.py
index c8acff371..a6b3608ee 100644
--- a/jperm/utils.py
+++ b/jperm/utils.py
@@ -51,6 +51,7 @@ def gen_keys():
private_key = os.path.join(key_path_dir, 'id_rsa')
public_key = os.path.join(key_path_dir, 'id_rsa.pub')
key.write_private_key_file(private_key)
+ os.chmod(private_key, 0644)
with open(public_key, 'w') as content_file:
for data in [key.get_name(),
diff --git a/jperm/views.py b/jperm/views.py
index f961a8e07..1c9eaa15c 100644
--- a/jperm/views.py
+++ b/jperm/views.py
@@ -2,8 +2,6 @@
from django.db.models import Q
from jperm.perm_api import *
-from jperm.models import PermLog as Log
-from jperm.models import SysUser
from juser.user_api import gen_ssh_key
@@ -380,8 +378,6 @@ def perm_role_edit(request):
return my_render('jperm/perm_role_list.html', locals(), request)
-
-
@require_role('admin')
def perm_role_push(request):
"""
@@ -460,108 +456,3 @@ def perm_role_push(request):
else:
return HttpResponse(u"推送系统角色: %s" % ','.join(role_names))
-
-
-
-
-
-
-
-
-
-@require_role('admin')
-def perm_group_list(request):
- header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权'
- keyword = request.GET.get('search', '')
- user_groups_list = UserGroup.objects.all()
- if keyword:
- request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword))
- user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request)
- return my_render('jperm/perm_group_list.html', locals(), request)
-
-
-
-@require_role('admin')
-def perm_group_edit(request):
- header_title, path1, path2 = '用户组授权', '授权管理', '授权更改'
- user_group_id = request.GET.get('id', '')
- user_group = get_object(UserGroup, id=user_group_id)
- asset_all = Asset.objects.all()
- asset_group_all = AssetGroup.objects.all()
- asset_permed = user_group.asset.all() # 获取授权的资产对象列表
- asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表
- if request.method == 'GET' and user_group:
- assets = [asset for asset in asset_all if asset not in asset_permed]
- asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed]
- return my_render('jperm/perm_group_edit.html', locals(), request)
- elif request.method == 'POST' and user_group:
- asset_id_select = request.POST.getlist('asset_select', [])
- asset_group_id_select = request.POST.getlist('asset_groups_select', [])
- asset_select = get_object_list(Asset, asset_id_select)
- asset_group_select = get_object_list(AssetGroup, asset_group_id_select)
- asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表
- asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表
- asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表
- asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表
- users = user_group.user_set.all()
- perm_info = {
- 'action': 'perm group edit: ' + user_group.name,
- 'del': {'users': users, 'assets': asset_del},
- 'new': {'users': users, 'assets': asset_new}
- }
- results = perm_user_api(perm_info)
- unreachable_asset = []
- failures_asset = []
- for ip in results.get('unreachable'):
- unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip)))
- for ip in results.get('failures'):
- failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip)))
- failures_asset.extend(unreachable_asset) # 失败的授权要统计
- for asset in failures_asset:
- if asset in asset_select:
- asset_select.remove(asset)
- else:
- asset_select.append(asset)
- user_group.asset = asset_select
- user_group.asset_group = asset_group_select
- user_group.save() # 保存到数据库
- return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json")
- else:
- return HttpResponse('输入错误')
-
-
-def log(request):
- header_title, path1, path2 = '授权记录', '授权管理', '授权记录'
- log_all = Log.objects.all().order_by('-datetime')
- log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request)
- return my_render('jperm/perm_log.html', locals(), request)
-
-
-def sys_user_add(request):
- asset_group_all = AssetGroup.objects.all()
- if request.method == 'POST':
- username = request.POST.get('username', '')
- password = request.POST.get('password', '')
- asset_groups_id = request.POST.getlist('asset_groups_select', [])
- comment = request.POST.get('comment')
- sys_user = SysUser(username=username, password=password, comment=comment)
- sys_user.save()
- gen_ssh_key(username, key_dir=os.path.join(SSH_KEY_DIR, 'sysuser'), authorized_keys=False)
- results = push_user(sys_user, asset_groups_id)
- return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json")
- return my_render('jperm/sys_user_add.html', locals(), request)
-
-
-def sys_user_list(request):
- users_list = SysUser.objects.all()
- users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request)
- return my_render('jperm/sys_user_list.html', locals(), request)
-
-
-def sys_user_edit(request):
- pass
-
-
-def sys_user_del(request):
- pass
-
diff --git a/jumpserver/api.py b/jumpserver/api.py
index 6dc1a1602..3ffa7566b 100644
--- a/jumpserver/api.py
+++ b/jumpserver/api.py
@@ -3,24 +3,27 @@
import os, sys, time, re
from Crypto.Cipher import AES
import crypt
+import pwd
from binascii import b2a_hex, a2b_hex
import hashlib
import datetime
import random
import subprocess
-from settings import *
+import json
+import logging
+from settings import *
from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.http import HttpResponse, Http404
from django.template import RequestContext
from juser.models import User, UserGroup
-from jlog.models import Log
+from jlog.models import Log, TtyLog
from jasset.models import Asset, AssetGroup
+from jperm.models import PermRule, PermRole
+from jumpserver.models import Setting
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.core.mail import send_mail
-import json
-import logging
def set_log(level):
@@ -28,11 +31,15 @@ def set_log(level):
return a log file object
根据提示设置log打印
"""
+ log_file = os.path.join(LOG_DIR, 'jumpserver.log')
+ if not os.path.isfile(log_file):
+ os.mknod(log_file)
+ os.chmod(log_file, 0777)
log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR,
'critical': logging.CRITICAL}
logger_f = logging.getLogger('jumpserver')
logger_f.setLevel(logging.DEBUG)
- fh = logging.FileHandler(os.path.join(LOG_DIR, 'jumpserver.log'))
+ fh = logging.FileHandler(log_file)
fh.setLevel(log_level_total.get(level, logging.DEBUG))
formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
@@ -40,6 +47,60 @@ def set_log(level):
return logger_f
+def get_asset_info(asset):
+ default = get_object(Setting, name='default')
+ info = {'hostname': asset.hostname, 'ip': asset.ip}
+ if asset.use_default_auth:
+ if default:
+ info['port'] = default.default_port
+ info['username'] = default.default_user
+ info['password'] = CRYPTOR.decrypt(default.default_password)
+ info['ssh_key'] = default.default_pri_key_path
+ else:
+ info['port'] = asset.port
+ info['username'] = asset.username
+ info['password'] = asset.password
+
+ return info
+
+
+def get_role(user, asset):
+ roles = []
+ rules = PermRule.objects.filter(user=user, asset=asset)
+ for rule in rules:
+ roles.extend(list(rule.role.all()))
+ return roles
+
+
+def get_role_key(user, role):
+ """
+ 由于role的key的权限是所有人可以读的, ansible要求为600,所以拷贝一份到特殊目录
+ :param user:
+ :param role:
+ :return: self key path
+ """
+ user_role_key_dir = os.path.join(KEY_DIR, 'user')
+ user_role_key_path = os.path.join(user_role_key_dir, '%s_%s.pem' % (user.username, role.name))
+ mkdir(user_role_key_dir, mode=777)
+ if not os.path.isfile(user_role_key_path):
+ with open(os.path.join(role.key_path, 'id_rsa')) as fk:
+ with open(user_role_key_path, 'w') as fu:
+ fu.write(fk.read())
+
+ print user_role_key_path, user.username
+ chown(user_role_key_path, user.username)
+ os.chmod(user_role_key_path, 0600)
+ return user_role_key_path
+
+
+def chown(path, user, group=''):
+ if not group:
+ group = user
+ uid = pwd.getpwnam(user).pw_uid
+ gid = pwd.getpwnam(group).pwd_gid
+ os.chown(path, uid, gid)
+
+
def page_list_return(total, current=1):
"""
page
@@ -56,7 +117,7 @@ def pages(post_objects, request):
page public function , return page's object tuple
分页公用函数,返回分页的对象元组
"""
- paginator = Paginator(post_objects, 10)
+ paginator = Paginator(post_objects, 20)
try:
current_page = int(request.GET.get('page', '1'))
except ValueError:
@@ -157,8 +218,7 @@ class PyCrypt(object):
try:
plain_text = cryptor.decrypt(a2b_hex(text))
except TypeError:
- # raise ServerError('Decrypt password error, TYpe error.')
- pass
+ raise ServerError('Decrypt password error, TYpe error.')
return plain_text.rstrip('\0')
@@ -386,16 +446,16 @@ def bash(cmd):
return subprocess.call(cmd, shell=True)
-def mkdir(dir_name, username='root', mode=0755):
+def mkdir(dir_name, username='', mode=0755):
"""
insure the dir exist and mode ok
目录存在,如果不存在就建立,并且权限正确
"""
if not os.path.isdir(dir_name):
os.makedirs(dir_name)
- os.chmod(dir_name, mode)
+ os.chmod(dir_name, mode)
if username:
- bash('chown %s:%s %s' % (username, username, dir_name))
+ chown(dir_name, username)
def http_success(request, msg):
diff --git a/juser/user_api.py b/juser/user_api.py
index 87e7b5cc5..0248e5c6d 100644
--- a/juser/user_api.py
+++ b/juser/user_api.py
@@ -130,7 +130,7 @@ def gen_ssh_key(username, password='',
"""
logger.debug('生成ssh key, 并设置authorized_keys')
private_key_file = os.path.join(key_dir, username)
- mkdir(key_dir)
+ mkdir(key_dir, mode=777)
if os.path.isfile(private_key_file):
os.unlink(private_key_file)
ret = bash('echo -e "y\n"|ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password))
@@ -143,7 +143,7 @@ def gen_ssh_key(username, password='',
with open(authorized_key_file, 'w') as auth_f:
auth_f.write(pub_f.read())
os.chmod(authorized_key_file, 0600)
- bash('chown %s:%s %s' % (username, username, authorized_key_file))
+ chown(authorized_key_file, username)
def server_add_user(username, password, ssh_key_pwd, ssh_key_login_need):
diff --git a/static/files/excels/cmdb_excel_2015_11_14_11_38.xlsx b/static/files/excels/cmdb_excel_2015_11_14_11_38.xlsx
new file mode 100644
index 000000000..7be819d90
Binary files /dev/null and b/static/files/excels/cmdb_excel_2015_11_14_11_38.xlsx differ
diff --git a/static/files/excels/cmdb_excel_2015_11_14_11_39.xlsx b/static/files/excels/cmdb_excel_2015_11_14_11_39.xlsx
new file mode 100644
index 000000000..831f40144
Binary files /dev/null and b/static/files/excels/cmdb_excel_2015_11_14_11_39.xlsx differ
diff --git a/static/files/excels/cmdb_excel_2015_11_19_22_05.xlsx b/static/files/excels/cmdb_excel_2015_11_19_22_05.xlsx
new file mode 100644
index 000000000..0c2778b89
Binary files /dev/null and b/static/files/excels/cmdb_excel_2015_11_19_22_05.xlsx differ
diff --git a/static/js/base.js b/static/js/base.js
index 15b54dd49..a8aa99b70 100644
--- a/static/js/base.js
+++ b/static/js/base.js
@@ -86,17 +86,17 @@ function move(from, to, from_o, to_o) {
});
}
-//function move_left(from, to, from_o, to_o) {
-// $("#" + from + " option").each(function () {
-// if ($(this).prop("selected") == true) {
-// $("#" + to).append(this);
-// if( typeof from_o !== 'undefined'){
-// $("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']"));
-// }
-// }
-// $(this).attr("selected",'true');
-// });
-//}
+function move_left(from, to, from_o, to_o) {
+ $("#" + from + " option").each(function () {
+ if ($(this).prop("selected") == true) {
+ $("#" + to).append(this);
+ if( typeof from_o !== 'undefined'){
+ $("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']"));
+ }
+ }
+ $(this).attr("selected",'true');
+ });
+}
//function move_all(from, to) {
// $("#" + from).children().each(function () {
diff --git a/static/js/layer/extend/layer.ext.js b/static/js/layer/extend/layer.ext.js
old mode 100644
new mode 100755
index 2b3a23b4e..80cd0ab12
--- a/static/js/layer/extend/layer.ext.js
+++ b/static/js/layer/extend/layer.ext.js
@@ -1,12 +1,2 @@
-/**
-
- @Name: layer拓展类,依赖于layer
- @Date: 2014.08.13
- @Author: 贤心
- @Versions:1.8.5-ext
- @Api:http://sentsin.com/jquery/layer
- @Desc: 本拓展会持续更新
-
- */
-
-layer.use("skin/layer.ext.css",function(){layer.ext&&layer.ext()}),layer.prompt=function(a,b,c){var d={},a=a||{},e={area:["auto","auto"],offset:[a.top||"",""],title:a.title||"信息",dialog:{btns:2,type:-1,msg:'',yes:function(c){var e=d.prompt.val();""===e?d.prompt.focus():e.replace(/\s/g,"").length>(a.length||1e3)?layer.tips("最多输入"+(a.length||1e3)+"个字数","#xubox_prompt",2):b&&b(e,c,d.prompt)},no:c},success:function(){d.prompt=$("#xubox_prompt"),d.prompt.focus()}};return 3===a.type&&(e.dialog.msg='"),$.layer(e)},layer.tab=function(a){var a=a||{},c=a.data||[],d={type:1,border:[0],area:["auto","auto"],bgcolor:"",title:!1,shade:a.shade,offset:a.offset,move:".xubox_tabmove",closeBtn:!1,page:{html:'
'}()+'
'+'
'+function(){var a=c.length,b=1,d="";if(a>0)for(d=''+c[0].title+"";a>b;b++)d+=""+c[b].title+"";return d}()+"
"+'
'+function(){var a=c.length,b=1,d="";if(a>0)for(d='- '+(c[0].content||"content未传入")+"
";a>b;b++)d+='- '+(c[b].content||"content未传入")+"
";return d}()+"
"+'
X'+"
"},success:function(a){var b=$(".xubox_tabtit").children(),c=$(".xubox_tab_main").children(),d=$(".xubox_tabclose");b.on("click",function(){var a=$(this),b=a.index();a.addClass("xubox_tabnow").siblings().removeClass("xubox_tabnow"),c.eq(b).show().siblings().hide()}),d.on("click",function(){layer.close(a.attr("times"))})}};return $.layer(d)},layer.photos=function(a){var b,c,d,e,f,g,h,i;if(a=a||{},b={imgIndex:1,end:null,html:$("html")},c=$(window),d=a.json,e=a.page,d){if(f=d.data,1!==d.status)return layer.msg("未请求到数据",2,8),void 0;if(b.imgLen=f.length,!(f.length>0))return layer.msg("没有任何图片",2,8),void 0;b.thissrc=f[d.start].src,b.pid=f[d.start].pid,b.imgsname=d.title||"",b.name=f[d.start].name,b.imgIndex=d.start+1}else g=$(e.parent).find("img"),h=g.eq(e.start),b.thissrc=h.attr("layer-img")||h.attr("src"),b.pid=h.attr("pid"),b.imgLen=g.length,b.imgsname=e.title||"",b.name=h.attr("alt"),b.imgIndex=e.start+1;return i={type:1,border:[0],area:[(a.html?915:600)+"px","auto"],title:!1,shade:[.9,"#000",!0],shadeClose:!0,offset:["25px",""],bgcolor:"",page:{html:'
'+function(){return b.imgLen>1?'
':""}()+'
"+function(){return a.html?''+a.html+"
":""}()},success:function(a){b.bigimg=a.find(".xubox_bigimg"),b.imgsee=b.bigimg.find(".xubox_imgsee"),b.imgbar=b.imgsee.find(".xubox_imgbar"),b.imgtit=b.imgbar.find(".xubox_imgtit"),b.layero=a;var c=b.imgs=b.bigimg.find("img");clearTimeout(b.timerr),b.timerr=setTimeout(function(){$("html").css("overflow","hidden").attr("layer-full",b.index)},10),c.load(function(){b.imgarea=[c.outerWidth(),c.outerHeight()],b.resize(a)}),b.event()},end:function(){layer.closeAll(),b.end=!0}},b.event=function(){b.bigimg.hover(function(){b.imgsee.show()},function(){b.imgsee.hide()}),i.imgprev=function(){b.imgIndex--,b.imgIndex<1&&(b.imgIndex=b.imgLen),b.tabimg()},b.bigimg.find(".xubox_prev").on("click",function(a){a.preventDefault(),i.imgprev()}),i.imgnext=function(){b.imgIndex++,b.imgIndex>b.imgLen&&(b.imgIndex=1),b.tabimg()},b.bigimg.find(".xubox_next").on("click",function(a){a.preventDefault(),i.imgnext()}),$(document).keyup(function(a){if(!b.end){var c=a.keyCode;a.preventDefault(),37===c?i.imgprev():39===c?i.imgnext():27===c&&layer.close(b.index)}}),b.tabimg=function(){var e,h,i,j,k;b.imgs.removeAttr("style"),d?(j=f[b.imgIndex-1],e=j.src,h=j.pid,i=j.name):(k=g.eq(b.imgIndex-1),e=k.attr("layer-img")||k.attr("src"),h=k.attr("layer-pid")||"",i=k.attr("alt")||""),b.imgs.attr({src:e,"layer-pid":h,alt:i}),b.imgtit.find("em").text(b.imgIndex+"/"+b.imgLen),b.imgsee.show(),a.tab&&a.tab({pid:h,name:i})}},b.resize=function(d){var g,e={},f=[c.width(),c.height()];e.limit=f[0]-f[0]/f[1]*(60*f[0]/f[1]),e.limit<600&&(e.limit=600),g=[e.limit,f[1]>400?f[1]-50:400],g[0]=a.html?g[0]:g[0]-300,layer.area(b.index,{width:g[0]+(a.html?15:0),height:g[1]}),e.flwidth=g[0]-(a.html?300:0),b.imgarea[0]>e.flwidth?b.imgs.css({width:e.flwidth}):b.imgs.css({width:b.imgarea[0]}),b.imgs.outerHeight()'+(a.value||"")+"":function(){return''}();return layer.open($.extend({btn:["确定","取消"],content:e,skin:"layui-layer-prompt"+b("prompt"),success:function(a){d=a.find(".layui-layer-input"),d.focus()},yes:function(b){var e=d.val();""===e?d.focus():e.length>(a.maxlength||500)?layer.tips("最多输入"+(a.maxlength||500)+"个字数",d,{tips:1}):c&&c(e,b,d)}},a))},layer.tab=function(a){a=a||{};var c=a.tab||{};return layer.open($.extend({type:1,skin:"layui-layer-tab"+b("tab"),title:function(){var a=c.length,b=1,d="";if(a>0)for(d=''+c[0].title+"";a>b;b++)d+=""+c[b].title+"";return d}(),content:''+function(){var a=c.length,b=1,d="";if(a>0)for(d='- '+(c[0].content||"no content")+"
";a>b;b++)d+='- '+(c[b].content||"no content")+"
";return d}()+"
",success:function(a){var b=a.find(".layui-layer-title").children(),c=a.find(".layui-layer-tabmain").children();b.on("mousedown",function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0;var b=$(this),d=b.index();b.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),c.eq(d).show().siblings().hide()})}},a))},layer.photos=function(a,c,d){function e(a,b,c){var d=new Image;d.onload=function(){d.onload=null,b(d)},d.onerror=function(a){d.onerror=null,c(a)},d.src=a}var f={};if(a=a||{},a.photos){var g=a.photos.constructor===Object,h=g?a.photos:{},i=h.data||[],j=h.start||0;if(f.imgIndex=j+1,g){if(0===i.length)return void layer.msg("没有图片")}else{var k=$(a.photos),l=k.find(a.img||"img");if(0===l.length)return;if(c||k.find(h.img||"img").each(function(b){var c=$(this);i.push({alt:c.attr("alt"),pid:c.attr("layer-pid"),src:c.attr("layer-src")||c.attr("src"),thumb:c.attr("src")}),c.on("click",function(){layer.photos($.extend(a,{photos:{start:b,data:i,tab:a.tab},full:a.full}),!0)})}),!c)return}f.imgprev=function(a){f.imgIndex--,f.imgIndex<1&&(f.imgIndex=i.length),f.tabimg(a)},f.imgnext=function(a,b){f.imgIndex++,f.imgIndex>i.length&&(f.imgIndex=1,b)||f.tabimg(a)},f.keyup=function(a){if(!f.end){var b=a.keyCode;a.preventDefault(),37===b?f.imgprev(!0):39===b?f.imgnext(!0):27===b&&layer.close(f.index)}},f.tabimg=function(b){i.length<=1||(h.start=f.imgIndex-1,layer.close(f.index),layer.photos(a,!0,b))},f.event=function(){f.bigimg.hover(function(){f.imgsee.show()},function(){f.imgsee.hide()}),f.bigimg.find(".layui-layer-imgprev").on("click",function(a){a.preventDefault(),f.imgprev()}),f.bigimg.find(".layui-layer-imgnext").on("click",function(a){a.preventDefault(),f.imgnext()}),$(document).on("keyup",f.keyup)},f.loadi=layer.load(1,{shade:"shade"in a?!1:.9,scrollbar:!1}),e(i[j].src,function(c){layer.close(f.loadi),f.index=layer.open($.extend({type:1,area:function(){var b=[c.width,c.height],d=[$(window).width()-100,$(window).height()-100];return!a.full&&b[0]>d[0]&&(b[0]=d[0],b[1]=b[0]*d[1]/b[0]),[b[0]+"px",b[1]+"px"]}(),title:!1,shade:.9,shadeClose:!0,closeBtn:!1,move:".layui-layer-phimg img",moveType:1,scrollbar:!1,moveOut:!0,shift:5*Math.random()|0,skin:"layui-layer-photos"+b("photos"),content:'!['+(i[j].alt||]('+i[j].src+')
",success:function(b,c){f.bigimg=b.find(".layui-layer-phimg"),f.imgsee=b.find(".layui-layer-imguide,.layui-layer-imgbar"),f.event(b),a.tab&&a.tab(i[j],b)},end:function(){f.end=!0,$(document).off("keyup",f.keyup)}},a))},function(){layer.close(f.loadi),layer.msg("当前图片地址异常
是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){i.length>1&&f.imgnext(!0,!0)}})})}}}();
\ No newline at end of file
diff --git a/static/js/layer/layer.js b/static/js/layer/layer.js
new file mode 100755
index 000000000..8536abf06
--- /dev/null
+++ b/static/js/layer/layer.js
@@ -0,0 +1,2 @@
+/*! layer-v2.1 弹层组件 License LGPL http://layer.layui.com/ By 贤心 */
+;!function(a,b){"use strict";var c,d,e={getPath:function(){var a=document.scripts,b=a[a.length-1],c=b.src;if(!b.getAttribute("merge"))return c.substring(0,c.lastIndexOf("/")+1)}(),enter:function(a){13===a.keyCode&&a.preventDefault()},config:{},end:{},btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"]},f={v:"2.1",ie6:!!a.ActiveXObject&&!a.XMLHttpRequest,index:0,path:e.getPath,config:function(a,b){var d=0;return a=a||{},f.cache=e.config=c.extend(e.config,a),f.path=e.config.path||f.path,"string"==typeof a.extend&&(a.extend=[a.extend]),f.use("skin/layer.css",a.extend&&a.extend.length>0?function g(){var c=a.extend;f.use(c[c[d]?d:d-1],d'+(i?f.title[0]:f.title)+"":"";return f.zIndex=g,b([f.shade?'':"",''+(a&&2!=f.type?"":k)+'
'+(0==f.type&&-1!==f.icon?'':"")+(1==f.type&&a?"":f.content||"")+'
'+function(){var a=j?'':"";return f.closeBtn&&(a+=''),a}()+""+(f.btn?function(){var a="";"string"==typeof f.btn&&(f.btn=[f.btn]);for(var b=0,c=f.btn.length;c>b;b++)a+='
'+f.btn[b]+"";return'
'+a+"
"}():"")+"
"],k),c},g.pt.creat=function(){var a=this,b=a.config,g=a.index,i=b.content,j="object"==typeof i;switch("string"==typeof b.area&&(b.area="auto"===b.area?["",""]:[b.area,""]),b.type){case 0:b.btn="btn"in b?b.btn:e.btn[0],f.closeAll("dialog");break;case 2:var i=b.content=j?b.content:[b.content||"http://layer.layui.com","auto"];b.content='';break;case 3:b.title=!1,b.closeBtn=!1,-1===b.icon&&0===b.icon,f.closeAll("loading");break;case 4:j||(b.content=[b.content,"body"]),b.follow=b.content[1],b.content=b.content[0]+'',b.title=!1,b.shade=!1,b.fix=!1,b.tips="object"==typeof b.tips?b.tips:[b.tips,!0],b.tipsMore||f.closeAll("tips")}a.vessel(j,function(d,e){c("body").append(d[0]),j?function(){2==b.type||4==b.type?function(){c("body").append(d[1])}():function(){i.parents("."+h[0])[0]||(i.show().addClass("layui-layer-wrap").wrap(d[1]),c("#"+h[0]+g).find("."+h[5]).before(e))}()}():c("body").append(d[1]),a.layero=c("#"+h[0]+g),b.scrollbar||h.html.css("overflow","hidden").attr("layer-full",g)}).auto(g),2==b.type&&f.ie6&&a.layero.find("iframe").attr("src",i[0]),c(document).off("keydown",e.enter).on("keydown",e.enter),a.layero.on("keydown",function(a){c(document).off("keydown",e.enter)}),4==b.type?a.tips():a.offset(),b.fix&&d.on("resize",function(){a.offset(),(/^\d+%$/.test(b.area[0])||/^\d+%$/.test(b.area[1]))&&a.auto(g),4==b.type&&a.tips()}),b.time<=0||setTimeout(function(){f.close(a.index)},b.time),a.move().callback()},g.pt.auto=function(a){function b(a){a=g.find(a),a.height(i[1]-j-k-2*(0|parseFloat(a.css("padding"))))}var e=this,f=e.config,g=c("#"+h[0]+a);""===f.area[0]&&f.maxWidth>0&&(/MSIE 7/.test(navigator.userAgent)&&f.btn&&g.width(g.innerWidth()),g.outerWidth()>f.maxWidth&&g.width(f.maxWidth));var i=[g.innerWidth(),g.innerHeight()],j=g.find(h[1]).outerHeight()||0,k=g.find("."+h[6]).outerHeight()||0;switch(f.type){case 2:b("iframe");break;default:""===f.area[1]?f.fix&&i[1]>=d.height()&&(i[1]=d.height(),b("."+h[5])):b("."+h[5])}return e},g.pt.offset=function(){var a=this,b=a.config,c=a.layero,e=[c.outerWidth(),c.outerHeight()],f="object"==typeof b.offset;a.offsetTop=(d.height()-e[1])/2,a.offsetLeft=(d.width()-e[0])/2,f?(a.offsetTop=b.offset[0],a.offsetLeft=b.offset[1]||a.offsetLeft):"auto"!==b.offset&&(a.offsetTop=b.offset,"rb"===b.offset&&(a.offsetTop=d.height()-e[1],a.offsetLeft=d.width()-e[0])),b.fix||(a.offsetTop=/%$/.test(a.offsetTop)?d.height()*parseFloat(a.offsetTop)/100:parseFloat(a.offsetTop),a.offsetLeft=/%$/.test(a.offsetLeft)?d.width()*parseFloat(a.offsetLeft)/100:parseFloat(a.offsetLeft),a.offsetTop+=d.scrollTop(),a.offsetLeft+=d.scrollLeft()),c.css({top:a.offsetTop,left:a.offsetLeft})},g.pt.tips=function(){var a=this,b=a.config,e=a.layero,f=[e.outerWidth(),e.outerHeight()],g=c(b.follow);g[0]||(g=c("body"));var i={width:g.outerWidth(),height:g.outerHeight(),top:g.offset().top,left:g.offset().left},j=e.find(".layui-layer-TipsG"),k=b.tips[0];b.tips[1]||j.remove(),i.autoLeft=function(){i.left+f[0]-d.width()>0?(i.tipLeft=i.left+i.width-f[0],j.css({right:12,left:"auto"})):i.tipLeft=i.left},i.where=[function(){i.autoLeft(),i.tipTop=i.top-f[1]-10,j.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",b.tips[1])},function(){i.tipLeft=i.left+i.width+10,i.tipTop=i.top,j.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",b.tips[1])},function(){i.autoLeft(),i.tipTop=i.top+i.height+10,j.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",b.tips[1])},function(){i.tipLeft=i.left-f[0]-10,i.tipTop=i.top,j.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",b.tips[1])}],i.where[k-1](),1===k?i.top-(d.scrollTop()+f[1]+16)<0&&i.where[2]():2===k?d.width()-(i.left+i.width+f[0]+16)>0||i.where[3]():3===k?i.top-d.scrollTop()+i.height+f[1]+16-d.height()>0&&i.where[0]():4===k&&f[0]+16-i.left>0&&i.where[1](),e.find("."+h[5]).css({"background-color":b.tips[1],"padding-right":b.closeBtn?"30px":""}),e.css({left:i.tipLeft,top:i.tipTop})},g.pt.move=function(){var a=this,b=a.config,e={setY:0,moveLayer:function(){var a=e.layero,b=parseInt(a.css("margin-left")),c=parseInt(e.move.css("left"));0===b||(c-=b),"fixed"!==a.css("position")&&(c-=a.parent().offset().left,e.setY=0),a.css({left:c,top:parseInt(e.move.css("top"))-e.setY})}},f=a.layero.find(b.move);return b.move&&f.attr("move","ok"),f.css({cursor:b.move?"move":"auto"}),c(b.move).on("mousedown",function(a){if(a.preventDefault(),"ok"===c(this).attr("move")){e.ismove=!0,e.layero=c(this).parents("."+h[0]);var f=e.layero.offset().left,g=e.layero.offset().top,i=e.layero.outerWidth()-6,j=e.layero.outerHeight()-6;c("#layui-layer-moves")[0]||c("body").append(''),e.move=c("#layui-layer-moves"),b.moveType&&e.move.css({visibility:"hidden"}),e.moveX=a.pageX-e.move.position().left,e.moveY=a.pageY-e.move.position().top,"fixed"!==e.layero.css("position")||(e.setY=d.scrollTop())}}),c(document).mousemove(function(a){if(e.ismove){var c=a.pageX-e.moveX,f=a.pageY-e.moveY;if(a.preventDefault(),!b.moveOut){e.setY=d.scrollTop();var g=d.width()-e.move.outerWidth(),h=e.setY;0>c&&(c=0),c>g&&(c=g),h>f&&(f=h),f>d.height()-e.move.outerHeight()+e.setY&&(f=d.height()-e.move.outerHeight()+e.setY)}e.move.css({left:c,top:f}),b.moveType&&e.moveLayer(),c=f=g=h=null}}).mouseup(function(){try{e.ismove&&(e.moveLayer(),e.move.remove(),b.moveEnd&&b.moveEnd()),e.ismove=!1}catch(a){e.ismove=!1}}),a},g.pt.callback=function(){function a(){var a=g.cancel&&g.cancel(b.index);a===!1||f.close(b.index)}var b=this,d=b.layero,g=b.config;b.openLayer(),g.success&&(2==g.type?d.find("iframe").on("load",function(){g.success(d,b.index)}):g.success(d,b.index)),f.ie6&&b.IE6(d),d.find("."+h[6]).children("a").on("click",function(){var e=c(this).index();g["btn"+(e+1)]&&g["btn"+(e+1)](b.index,d),0===e?g.yes?g.yes(b.index,d):f.close(b.index):1===e?a():g["btn"+(e+1)]||f.close(b.index)}),d.find("."+h[7]).on("click",a),g.shadeClose&&c("#layui-layer-shade"+b.index).on("click",function(){f.close(b.index)}),d.find(".layui-layer-min").on("click",function(){f.min(b.index,g),g.min&&g.min(d)}),d.find(".layui-layer-max").on("click",function(){c(this).hasClass("layui-layer-maxmin")?(f.restore(b.index),g.restore&&g.restore(d)):(f.full(b.index,g),g.full&&g.full(d))}),g.end&&(e.end[b.index]=g.end)},e.reselect=function(){c.each(c("select"),function(a,b){var d=c(this);d.parents("."+h[0])[0]||1==d.attr("layer")&&c("."+h[0]).length<1&&d.removeAttr("layer").show(),d=null})},g.pt.IE6=function(a){function b(){a.css({top:f+(e.config.fix?d.scrollTop():0)})}var e=this,f=a.offset().top;b(),d.scroll(b),c("select").each(function(a,b){var d=c(this);d.parents("."+h[0])[0]||"none"===d.css("display")||d.attr({layer:"1"}).hide(),d=null})},g.pt.openLayer=function(){var a=this;f.zIndex=a.config.zIndex,f.setTop=function(a){var b=function(){f.zIndex++,a.css("z-index",f.zIndex+1)};return f.zIndex=parseInt(a[0].style.zIndex),a.on("mousedown",b),f.zIndex}},e.record=function(a){var b=[a.outerWidth(),a.outerHeight(),a.position().top,a.position().left+parseFloat(a.css("margin-left"))];a.find(".layui-layer-max").addClass("layui-layer-maxmin"),a.attr({area:b})},e.rescollbar=function(a){h.html.attr("layer-full")==a&&(h.html[0].style.removeProperty?h.html[0].style.removeProperty("overflow"):h.html[0].style.removeAttribute("overflow"),h.html.removeAttr("layer-full"))},a.layer=f,f.getChildFrame=function(a,b){return b=b||c("."+h[4]).attr("times"),c("#"+h[0]+b).find("iframe").contents().find(a)},f.getFrameIndex=function(a){return c("#"+a).parents("."+h[4]).attr("times")},f.iframeAuto=function(a){if(a){var b=f.getChildFrame("html",a).outerHeight(),d=c("#"+h[0]+a),e=d.find(h[1]).outerHeight()||0,g=d.find("."+h[6]).outerHeight()||0;d.css({height:b+e+g}),d.find("iframe").css({height:b})}},f.iframeSrc=function(a,b){c("#"+h[0]+a).find("iframe").attr("src",b)},f.style=function(a,b){var d=c("#"+h[0]+a),f=d.attr("type"),g=d.find(h[1]).outerHeight()||0,i=d.find("."+h[6]).outerHeight()||0;(f===e.type[1]||f===e.type[2])&&(d.css(b),f===e.type[2]&&d.find("iframe").css({height:parseFloat(b.height)-g-i}))},f.min=function(a,b){var d=c("#"+h[0]+a),g=d.find(h[1]).outerHeight()||0;e.record(d),f.style(a,{width:180,height:g,overflow:"hidden"}),d.find(".layui-layer-min").hide(),"page"===d.attr("type")&&d.find(h[4]).hide(),e.rescollbar(a)},f.restore=function(a){var b=c("#"+h[0]+a),d=b.attr("area").split(",");b.attr("type");f.style(a,{width:parseFloat(d[0]),height:parseFloat(d[1]),top:parseFloat(d[2]),left:parseFloat(d[3]),overflow:"visible"}),b.find(".layui-layer-max").removeClass("layui-layer-maxmin"),b.find(".layui-layer-min").show(),"page"===b.attr("type")&&b.find(h[4]).show(),e.rescollbar(a)},f.full=function(a){var b,g=c("#"+h[0]+a);e.record(g),h.html.attr("layer-full")||h.html.css("overflow","hidden").attr("layer-full",a),clearTimeout(b),b=setTimeout(function(){var b="fixed"===g.css("position");f.style(a,{top:b?0:d.scrollTop(),left:b?0:d.scrollLeft(),width:d.width(),height:d.height()}),g.find(".layui-layer-min").hide()},100)},f.title=function(a,b){var d=c("#"+h[0]+(b||f.index)).find(h[1]);d.html(a)},f.close=function(a){var b=c("#"+h[0]+a),d=b.attr("type");if(b[0]){if(d===e.type[1]&&"object"===b.attr("conType")){b.children(":not(."+h[5]+")").remove();for(var g=0;2>g;g++)b.find(".layui-layer-wrap").unwrap().hide()}else{if(d===e.type[2])try{var i=c("#"+h[4]+a)[0];i.contentWindow.document.write(""),i.contentWindow.close(),b.find("."+h[5])[0].removeChild(i)}catch(j){}b[0].innerHTML="",b.remove()}c("#layui-layer-moves, #layui-layer-shade"+a).remove(),f.ie6&&e.reselect(),e.rescollbar(a),c(document).off("keydown",e.enter),"function"==typeof e.end[a]&&e.end[a](),delete e.end[a]}},f.closeAll=function(a){c.each(c("."+h[0]),function(){var b=c(this),d=a?b.attr("type")===a:1;d&&f.close(b.attr("times")),d=null})},e.run=function(){c=jQuery,d=c(a),h.html=c("html"),f.open=function(a){var b=new g(a);return b.index}},"function"==typeof define?define(function(){return e.run(),f}):function(){e.run(),f.use("skin/layer.css")}()}(window);
\ No newline at end of file
diff --git a/static/js/layer/layer.min.js b/static/js/layer/layer.min.js
deleted file mode 100644
index 4cd35e208..000000000
--- a/static/js/layer/layer.min.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/****************************************
-
- @Name:layer v1.8.5 弹层组件压缩版
- @Author:贤心
- @Date:2014-08-13
- @Blog:http://sentsin.com
- @Copyright:Sentsin Xu(贤心)
- @官网:http://sentsin.com/jquery/layer
-
- */
-
-;!function(a,b){
-"use strict";
-var c,d,
-e="", //组件存放目录,为空表示自动获取
-f={getPath:function(){var a=document.scripts,b=a[a.length-1].src;return e?e:b.substring(0,b.lastIndexOf("/")+1)},type:["dialog","page","iframe","loading","tips"]};a.layer={v:"1.8.5",ie6:!!a.ActiveXObject&&!a.XMLHttpRequest,index:0,path:f.getPath(),use:function(a,b){var d=c("head")[0],a=a.replace(/\s/g,""),e=/\.css$/.test(a),f=document.createElement(e?"link":"script"),g=a.replace(/\.|\//g,"");e&&(f.type="text/css",f.rel="stylesheet"),f[e?"href":"src"]=/^http:\/\//.test(a)?a:layer.path+a,f.id=g,c("#"+g)[0]||d.appendChild(f),b&&(document.all?c(f).ready(b):c(f).load(b))},alert:function(a,b,d,e){var f="function"==typeof d,g={dialog:{msg:a,type:b,yes:f?d:e},area:["auto","auto"]};return f||(g.title=d),c.layer(g)},confirm:function(a,b,d,e){var f="function"==typeof d,g={dialog:{msg:a,type:4,btns:2,yes:b,no:f?d:e}};return f||(g.title=d),c.layer(g)},msg:function(a,d,e,f){var g={title:!1,closeBtn:!1,time:d===b?2:d,dialog:{msg:""===a||a===b?" ":a},end:f};return"object"==typeof e?(g.dialog.type=e.type,g.shade=e.shade,g.shift=e.rate):"function"==typeof e?g.end=e:g.dialog.type=e,c.layer(g)},load:function(a,b){return"string"==typeof a?layer.msg(a,b||0,16):c.layer({time:a,loading:{type:b},bgcolor:b?"#fff":"",shade:b?[.1,"#000"]:[0],border:3!==b&&b?[6,.3,"#000"]:[0],type:3,title:["",!1],closeBtn:[0,!1]})},tips:function(a,b,d,e,f,g){var h={type:4,shade:!1,success:function(a){this.closeBtn||a.find(".xubox_tips").css({"padding-right":10})},bgcolor:"",tips:{msg:a,follow:b}};return h.time="object"==typeof d?d.time:0|d,d=d||{},h.closeBtn=d.closeBtn||!1,h.maxWidth=d.maxWidth||e,h.tips.guide=d.guide||f,h.tips.style=d.style||g,h.tips.more=d.more,c.layer(h)}};var g=["xubox_layer","xubox_iframe",".xubox_title",".xubox_text",".xubox_page",".xubox_main"],h=function(a){var b=this,d=b.config;layer.index++,b.index=layer.index,b.config=c.extend({},d,a),b.config.dialog=c.extend({},d.dialog,a.dialog),b.config.page=c.extend({},d.page,a.page),b.config.iframe=c.extend({},d.iframe,a.iframe),b.config.loading=c.extend({},d.loading,a.loading),b.config.tips=c.extend({},d.tips,a.tips),b.creat()};h.pt=h.prototype,h.pt.config={type:0,shade:[.3,"#000"],fix:!0,move:".xubox_title",title:"信息",offset:["","50%"],area:["310px","auto"],closeBtn:[0,!0],time:0,bgcolor:"#fff",border:[6,.3,"#000"],zIndex:19891014,maxWidth:400,dialog:{btns:1,btn:["确定","取消"],type:8,msg:"",yes:function(a){layer.close(a)},no:function(a){layer.close(a)}},page:{dom:"#xulayer",html:"",url:""},iframe:{src:"http://sentsin.com",scrolling:"auto"},loading:{type:0},tips:{msg:"",follow:"",guide:0,isGuide:!0,style:["background-color:#FF9900; color:#fff;","#FF9900"]},success:function(){},close:function(a){layer.close(a)},end:function(){}},h.pt.space=function(a){var b=this,a=a||"",c=b.index,d=b.config,e=d.dialog,f=-1===e.type?"":'',h=[''+f+''+e.msg+"
",''+a+"
",'','',''],i="",j="",k=d.zIndex+c,l="z-index:"+k+"; background-color:"+d.shade[1]+"; opacity:"+d.shade[0]+"; filter:alpha(opacity="+100*d.shade[0]+");";d.shade[0]&&(i=''),d.zIndex=k;var m="",n="",o="z-index:"+(k-1)+"; background-color: "+d.border[2]+"; opacity:"+d.border[1]+"; filter:alpha(opacity="+100*d.border[1]+"); top:-"+d.border[0]+"px; left:-"+d.border[0]+"px;";d.border[0]&&(j=''),!d.maxmin||1!==d.type&&2!==d.type||/^\d+%$/.test(d.area[0])&&/^\d+%$/.test(d.area[1])||(n=''),d.closeBtn[1]&&(n+='');var p="object"==typeof d.title;return d.title&&(m=''+(p?d.title[0]:d.title)+"
"),[i,''+h[d.type]+m+''+n+'
'+j+"
"]},h.pt.creat=function(){var a=this,b="",d=a.config,e=d.dialog,f=a.index,h=d.page,i=c("body"),j=function(d){var d=d||"";b=a.space(d),i.append(c(b[0]))};switch(d.type){case 0:d.title||(d.area=["auto","auto"]),c(".xubox_dialog")[0]&&layer.close(c(".xubox_dialog").parents("."+g[0]).attr("times"));break;case 1:if(""!==h.html)j(''+h.html+"
"),i.append(c(b[1]));else if(""!==h.url)j(''+h.html+"
"),i.append(c(b[1])),c.get(h.url,function(a){c("#xuboxPageHtml"+f).html(a.toString()),h.ok&&h.ok(a)});else{if(0!=c(h.dom).parents(g[4]).length)return;j(),c(h.dom).show().wrap(c(b[1]))}break;case 3:d.title=!1,d.area=["auto","auto"],d.closeBtn=["",!1],c(".xubox_loading")[0]&&layer.closeLoad();break;case 4:d.title=!1,d.area=["auto","auto"],d.fix=!1,d.border=[0],d.tips.more||layer.closeTips()}1!==d.type&&(j(),i.append(c(b[1])));var k=a.layerE=c("#"+g[0]+f);if(k.css({width:d.area[0],height:d.area[1]}),d.fix||k.css({position:"absolute"}),d.title&&(3!==d.type||4!==d.type)){var l=0===d.type?e:d,m=k.find(".xubox_botton");switch(l.btn=d.btn||e.btn,l.btns){case 0:m.html("").hide();break;case 1:m.html(''+l.btn[0]+"");break;case 2:m.html(''+l.btn[0]+''+l.btn[1]+"")}}"auto"===k.css("left")?(k.hide(),setTimeout(function(){k.show(),a.set(f)},500)):a.set(f),d.time<=0||a.autoclose(),a.callback()},f.fade=function(a,b,c){a.css({opacity:0}).animate({opacity:c},b)},h.pt.offset=function(){var a=this,b=a.config,c=a.layerE,e=c.outerHeight();a.offsetTop=""===b.offset[0]&&ee.maxWidth&&i.width(e.maxWidth),o.tipColor=e.tips.style[1],m[0]=i.outerWidth(),o.autoLeft=function(){o.left+m[0]-d.width()>0?(o.tipLeft=o.left+o.width-m[0],p.css({right:12,left:"auto"})):o.tipLeft=o.left},o.where=[function(){o.autoLeft(),o.tipTop=o.top-m[1]-10,p.removeClass("layerTipsB").addClass("layerTipsT").css({"border-right-color":o.tipColor})},function(){o.tipLeft=o.left+o.width+10,o.tipTop=o.top,p.removeClass("layerTipsL").addClass("layerTipsR").css({"border-bottom-color":o.tipColor})},function(){o.autoLeft(),o.tipTop=o.top+o.height+10,p.removeClass("layerTipsT").addClass("layerTipsB").css({"border-right-color":o.tipColor})},function(){o.tipLeft=o.left-m[0]+10,o.tipTop=o.top,p.removeClass("layerTipsR").addClass("layerTipsL").css({"border-bottom-color":o.tipColor})}],o.where[e.tips.guide](),0===e.tips.guide?o.top-(d.scrollTop()+m[1]+16)<0&&o.where[2]():1===e.tips.guide?d.width()-(o.left+o.width+m[0]+16)>0||o.where[3]():2===e.tips.guide?o.top-d.scrollTop()+o.height+m[1]+16-d.height()>0&&o.where[0]():3===e.tips.guide?m[0]+16-o.left>0&&o.where[1]():4===e.tips.guide,i.css({left:o.tipLeft,top:o.tipTop})}e.fadeIn&&(f.fade(i,e.fadeIn,1),f.fade(c("#xubox_shade"+a),e.fadeIn,e.shade[0])),e.fix&&""===e.offset[0]&&!e.shift&&d.on("resize",function(){i.css({top:(d.height()-i.outerHeight())/2})}),b.move()},h.pt.shift=function(a,b,c){var e=this,f=e.config,g=e.layerE,h=0,i=d.width(),j=d.height()+(f.fix?0:d.scrollTop());h="50%"==f.offset[1]||""==f.offset[1]?g.outerWidth()/2:g.outerWidth();var k={t:{top:e.offsetTop},b:{top:j-g.outerHeight()-f.border[0]},cl:h+f.border[0],ct:-g.outerHeight(),cr:i-h-f.border[0]};switch(a){case"left-top":g.css({left:k.cl,top:k.ct}).animate(k.t,b);break;case"top":g.css({top:k.ct}).animate(k.t,b);break;case"right-top":g.css({left:k.cr,top:k.ct}).animate(k.t,b);break;case"right-bottom":g.css({left:k.cr,top:j}).animate(c?k.t:k.b,b);break;case"bottom":g.css({top:j}).animate(c?k.t:k.b,b);break;case"left-bottom":g.css({left:k.cl,top:j}).animate(c?k.t:k.b,b);break;case"left":g.css({left:-g.outerWidth()}).animate({left:e.offsetLeft},b)}},h.pt.autoArea=function(a){var b,d=this,a=a||d.index,e=d.config,f=e.page,h=c("#"+g[0]+a),i=h.find(g[2]),j=h.find(g[5]),k=e.title?i.innerHeight():0,l=0;switch("auto"===e.area[0]&&j.outerWidth()>=e.maxWidth&&h.css({width:e.maxWidth}),e.type){case 0:var m=h.find(".xubox_botton>a");b=h.find(g[3]).outerHeight()+20,m.length>0&&(l=m.outerHeight()+20);break;case 1:var n=h.find(g[4]);b=c(f.dom).outerHeight(),"auto"===e.area[0]&&h.css({width:n.outerWidth()}),(""!==f.html||""!==f.url)&&(b=n.outerHeight());break;case 2:h.find("iframe").css({width:h.outerWidth(),height:h.outerHeight()-(e.title?i.innerHeight():0)});break;case 3:var o=h.find(".xubox_loading");b=o.outerHeight(),j.css({width:o.width()})}"auto"===e.area[1]&&j.css({height:k+b+l}),c("#xubox_border"+a).css({width:h.outerWidth()+2*e.border[0],height:h.outerHeight()+2*e.border[0]}),layer.ie6&&"auto"!==e.area[0]&&j.css({width:h.outerWidth()}),h.css("50%"!==e.offset[1]&&""!=e.offset[1]||4===e.type?{marginLeft:0}:{marginLeft:-h.outerWidth()/2})},h.pt.move=function(){var a=this,b=a.config,e={setY:0,moveLayer:function(){if(0==parseInt(e.layerE.css("margin-left")))var a=parseInt(e.move.css("left"));else var a=parseInt(e.move.css("left"))+-parseInt(e.layerE.css("margin-left"));"fixed"!==e.layerE.css("position")&&(a-=e.layerE.parent().offset().left,e.setY=0),e.layerE.css({left:a,top:parseInt(e.move.css("top"))-e.setY})}},f=a.layerE.find(b.move);b.move&&f.attr("move","ok"),f.css(b.move?{cursor:"move"}:{cursor:"auto"}),c(b.move).on("mousedown",function(a){if(a.preventDefault(),"ok"===c(this).attr("move")){e.ismove=!0,e.layerE=c(this).parents("."+g[0]);var f=e.layerE.offset().left,h=e.layerE.offset().top,i=e.layerE.width()-6,j=e.layerE.height()-6;c("#xubox_moves")[0]||c("body").append(''),e.move=c("#xubox_moves"),b.moveType&&e.move.css({opacity:0}),e.moveX=a.pageX-e.move.position().left,e.moveY=a.pageY-e.move.position().top,"fixed"!==e.layerE.css("position")||(e.setY=d.scrollTop())}}),c(document).mousemove(function(a){if(e.ismove){var c=a.pageX-e.moveX,f=a.pageY-e.moveY;if(a.preventDefault(),!b.moveOut){e.setY=d.scrollTop();var g=d.width()-e.move.outerWidth()-b.border[0],h=b.border[0]+e.setY;cg&&(c=g),h>f&&(f=h),f>d.height()-e.move.outerHeight()-b.border[0]+e.setY&&(f=d.height()-e.move.outerHeight()-b.border[0]+e.setY)}e.move.css({left:c,top:f}),b.moveType&&e.moveLayer(),c=null,f=null,g=null,h=null}}).mouseup(function(){try{e.ismove&&(e.moveLayer(),e.move.remove()),e.ismove=!1}catch(a){e.ismove=!1}b.moveEnd&&b.moveEnd()})},h.pt.autoclose=function(){var a=this,b=a.config.time,c=function(){b--,0===b&&(layer.close(a.index),clearInterval(a.autotime))};a.autotime=setInterval(c,1e3)},f.config={end:{}},h.pt.callback=function(){var a=this,b=a.layerE,d=a.config,e=d.dialog;a.openLayer(),a.config.success(b),layer.ie6&&a.IE6(b),b.find(".xubox_close").on("click",function(){d.close(a.index),layer.close(a.index)}),b.find(".xubox_yes").on("click",function(){d.yes?d.yes(a.index):e.yes(a.index)}),b.find(".xubox_no").on("click",function(){d.no?d.no(a.index):e.no(a.index),layer.close(a.index)}),a.config.shadeClose&&c("#xubox_shade"+a.index).on("click",function(){layer.close(a.index)}),b.find(".xubox_min").on("click",function(){layer.min(a.index,d),d.min&&d.min(b)}),b.find(".xubox_max").on("click",function(){c(this).hasClass("xubox_maxmin")?(layer.restore(a.index),d.restore&&d.restore(b)):(layer.full(a.index,d),d.full&&d.full(b))}),f.config.end[a.index]=d.end},f.reselect=function(){c.each(c("select"),function(){var a=c(this);a.parents("."+g[0])[0]||1==a.attr("layer")&&c("."+g[0]).length<1&&a.removeAttr("layer").show(),a=null})},h.pt.IE6=function(a){var b=this,e=a.offset().top;if(b.config.fix)var f=function(){a.css({top:d.scrollTop()+e})};else var f=function(){a.css({top:e})};f(),d.scroll(f),c.each(c("select"),function(){var a=c(this);a.parents("."+g[0])[0]||"none"==a.css("display")||a.attr({layer:"1"}).hide(),a=null})},h.pt.openLayer=function(){{var a=this;a.layerE}layer.autoArea=function(b){return a.autoArea(b)},layer.shift=function(b,c,d){a.shift(b,c,d)},layer.setMove=function(){return a.move()},layer.zIndex=a.config.zIndex,layer.setTop=function(a){var b=function(){layer.zIndex++,a.css("z-index",layer.zIndex+1)};return layer.zIndex=parseInt(a[0].style.zIndex),a.on("mousedown",b),layer.zIndex}},f.isauto=function(a,b,c){"auto"===b.area[0]&&(b.area[0]=a.outerWidth()),"auto"===b.area[1]&&(b.area[1]=a.outerHeight()),a.attr({area:b.area+","+c}),a.find(".xubox_max").addClass("xubox_maxmin")},f.rescollbar=function(a){g.html.attr("layer-full")==a&&(g.html[0].style.removeProperty?g.html[0].style.removeProperty("overflow"):g.html[0].style.removeAttribute("overflow"),g.html.removeAttr("layer-full"))},layer.getIndex=function(a){return c(a).parents("."+g[0]).attr("times")},layer.getChildFrame=function(a,b){return b=b||c("."+g[1]).parents("."+g[0]).attr("times"),c("#"+g[0]+b).find("."+g[1]).contents().find(a)},layer.getFrameIndex=function(a){return c(a?"#"+a:"."+g[1]).parents("."+g[0]).attr("times")},layer.iframeAuto=function(a){a=a||c("."+g[1]).parents("."+g[0]).attr("times");var b=layer.getChildFrame("body",a).outerHeight(),d=c("#"+g[0]+a),e=d.find(g[2]),f=0;e&&(f=e.height()),d.css({height:b+f});var h=-parseInt(c("#xubox_border"+a).css("top"));c("#xubox_border"+a).css({height:b+2*h+f}),c("#"+g[1]+a).css({height:b})},layer.iframeSrc=function(a,b){c("#"+g[0]+a).find("iframe").attr("src",b)},layer.area=function(a,b){var d=[c("#"+g[0]+a),c("#xubox_border"+a)],e=d[0].attr("type"),h=d[0].find(g[5]),i=d[0].find(g[2]);if(e===f.type[1]||e===f.type[2]){if(d[0].css(b),h.css({width:b.width,height:b.height}),e===f.type[2]){var j=d[0].find("iframe");j.css({width:b.width,height:i?b.height-i.innerHeight():b.height})}"0px"!==d[0].css("margin-left")&&(b.hasOwnProperty("top")&&d[0].css({top:b.top-(d[1][0]?parseFloat(d[1].css("top")):0)}),b.hasOwnProperty("left")&&d[0].css({left:b.left+d[0].outerWidth()/2-(d[1][0]?parseFloat(d[1].css("left")):0)}),d[0].css({marginLeft:-d[0].outerWidth()/2})),d[1][0]&&d[1].css({width:parseFloat(b.width)-2*parseFloat(d[1].css("left")),height:parseFloat(b.height)-2*parseFloat(d[1].css("top"))})}},layer.min=function(a,b){var d=c("#"+g[0]+a),e=[d.position().top,d.position().left+parseFloat(d.css("margin-left"))];f.isauto(d,b,e),layer.area(a,{width:180,height:35}),d.find(".xubox_min").hide(),"page"===d.attr("type")&&d.find(g[4]).hide(),f.rescollbar(a)},layer.restore=function(a){{var b=c("#"+g[0]+a),d=b.attr("area").split(",");b.attr("type")}layer.area(a,{width:parseFloat(d[0]),height:parseFloat(d[1]),top:parseFloat(d[2]),left:parseFloat(d[3])}),b.find(".xubox_max").removeClass("xubox_maxmin"),b.find(".xubox_min").show(),"page"===b.attr("type")&&b.find(g[4]).show(),f.rescollbar(a)},layer.full=function(a,b){var e,h=c("#"+g[0]+a),i=2*b.border[0]||6,j=[h.position().top,h.position().left+parseFloat(h.css("margin-left"))];f.isauto(h,b,j),g.html.attr("layer-full")||g.html.css("overflow","hidden").attr("layer-full",a),clearTimeout(e),e=setTimeout(function(){layer.area(a,{top:"fixed"===h.css("position")?0:d.scrollTop(),left:"fixed"===h.css("position")?0:d.scrollLeft(),width:d.width()-i,height:d.height()-i})},100)},layer.title=function(a,b){var d=c("#"+g[0]+(b||layer.index)).find(".xubox_title>em");d.html(a)},layer.close=function(a){var b=c("#"+g[0]+a),d=b.attr("type"),e=c("#xubox_moves, #xubox_shade"+a);if(b[0]){if(d==f.type[1])if(b.find(".xuboxPageHtml")[0])b[0].innerHTML="",b.remove();else{b.find(".xubox_setwin,.xubox_close,.xubox_botton,.xubox_title,.xubox_border").remove();for(var h=0;3>h;h++)b.find(".layer_pageContent").unwrap().hide()}else b[0].innerHTML="",b.remove();e.remove(),layer.ie6&&f.reselect(),f.rescollbar(a),"function"==typeof f.config.end[a]&&f.config.end[a](),delete f.config.end[a]}},layer.closeLoad=function(){layer.close(c(".xubox_loading").parents("."+g[0]).attr("times"))},layer.closeTips=function(){layer.closeAll("tips")},layer.closeAll=function(a){c.each(c("."+g[0]),function(){var b=c(this),d=a?b.attr("type")===a:1;d&&layer.close(b.attr("times")),d=null})},f.run=function(){c=jQuery,d=c(a),g.html=c("html"),layer.use("skin/layer.css"),c.layer=function(a){var b=new h(a);return b.index},(new Image).src=layer.path+"skin/default/xubox_ico0.png"},"function"==typeof define?define(function(){return f.run(),layer}):f.run()}(window);
\ No newline at end of file
diff --git a/static/js/layer/skin/default/icon-ext.png b/static/js/layer/skin/default/icon-ext.png
new file mode 100755
index 000000000..bbbb669bb
Binary files /dev/null and b/static/js/layer/skin/default/icon-ext.png differ
diff --git a/static/js/layer/skin/default/icon.png b/static/js/layer/skin/default/icon.png
new file mode 100755
index 000000000..3e17da8b1
Binary files /dev/null and b/static/js/layer/skin/default/icon.png differ
diff --git a/static/js/layer/skin/default/icon_ext.png b/static/js/layer/skin/default/icon_ext.png
deleted file mode 100644
index 8baee5979..000000000
Binary files a/static/js/layer/skin/default/icon_ext.png and /dev/null differ
diff --git a/static/js/layer/skin/default/xubox_loading0.gif b/static/js/layer/skin/default/loading-0.gif
old mode 100644
new mode 100755
similarity index 100%
rename from static/js/layer/skin/default/xubox_loading0.gif
rename to static/js/layer/skin/default/loading-0.gif
diff --git a/static/js/layer/skin/default/xubox_loading1.gif b/static/js/layer/skin/default/loading-1.gif
old mode 100644
new mode 100755
similarity index 100%
rename from static/js/layer/skin/default/xubox_loading1.gif
rename to static/js/layer/skin/default/loading-1.gif
diff --git a/static/js/layer/skin/default/xubox_loading2.gif b/static/js/layer/skin/default/loading-2.gif
old mode 100644
new mode 100755
similarity index 100%
rename from static/js/layer/skin/default/xubox_loading2.gif
rename to static/js/layer/skin/default/loading-2.gif
diff --git a/static/js/layer/skin/default/textbg.png b/static/js/layer/skin/default/textbg.png
deleted file mode 100644
index ad1040c42..000000000
Binary files a/static/js/layer/skin/default/textbg.png and /dev/null differ
diff --git a/static/js/layer/skin/default/xubox_ico0.png b/static/js/layer/skin/default/xubox_ico0.png
deleted file mode 100644
index 7754a47f7..000000000
Binary files a/static/js/layer/skin/default/xubox_ico0.png and /dev/null differ
diff --git a/static/js/layer/skin/default/xubox_loading3.gif b/static/js/layer/skin/default/xubox_loading3.gif
deleted file mode 100644
index fbe57be3c..000000000
Binary files a/static/js/layer/skin/default/xubox_loading3.gif and /dev/null differ
diff --git a/static/js/layer/skin/default/xubox_title0.png b/static/js/layer/skin/default/xubox_title0.png
deleted file mode 100644
index 4ffbe3155..000000000
Binary files a/static/js/layer/skin/default/xubox_title0.png and /dev/null differ
diff --git a/static/js/layer/skin/layer.css b/static/js/layer/skin/layer.css
old mode 100644
new mode 100755
index 4d04643f0..a3908881e
--- a/static/js/layer/skin/layer.css
+++ b/static/js/layer/skin/layer.css
@@ -1,83 +1,7 @@
-/**
+/*!
@Name: layer's style
- @Date: 2012.09.15
@Author: 贤心
- @blog: sentsin.com
+ @Blog: sentsin.com
-**/
-
-*html{background-image:url(about:blank); background-attachment:fixed;}
-
-/** common **/
-.xubox_shade, .xubox_layer{position:fixed; _position:absolute;}
-.xubox_shade{top:0; left:0; width:100%; height:100%; _height:expression(document.body.offsetHeight+"px");}
-.xubox_layer{top:150px; left:50%; height:auto; width:310px; margin-left:-155px;}
-.xubox_border, .xubox_title, .xubox_title i, .xubox_page, .xubox_iframe, .xubox_title em, .xubox_close, .xubox_msgico, .xubox_moves{position:absolute;}
-.xubox_border{border-radius: 5px;}
-.xubox_title{left:0; top:0;}
-.xubox_main{position:relative; height:100%; _float:left;}
-.xubox_page{top:0; left:0;}
-.xubox_load{background:url(default/xubox_loading0.gif) #fff center center no-repeat;}
-.xubox_loading{display:block; float:left; text-decoration:none; color:#FFF; _float:none; }
-.xulayer_png32{background:url(default/xubox_ico0.png) no-repeat;}
-.xubox_moves{border:3px solid #666; cursor:move; background-color:rgba(255,255,255,.3); background-color:#fff\9; filter:alpha(opacity=50);}
-
-.xubox_msgico{width:32px; height:32px; top:52px; left:15px; background:url(default/xubox_ico0.png) no-repeat;}
-.xubox_text{ padding-left:55px; float:left; line-height:25px; word-break:break-all; padding-right:20px; overflow:hidden; font-size:14px;}
-.xubox_msgtype0{background-position:-91px -38px;}
-.xubox_msgtype1{background-position:-128px -38px }
-.xubox_msgtype2{background-position:-163px -38px;}
-.xubox_msgtype3{background-position:-91px -75px;}
-.xubox_msgtype4{background-position:-163px -75px;}
-.xubox_msgtype5{background-position:-163px -112px;}
-.xubox_msgtype6{background-position:-163px -148px;}
-.xubox_msgtype7{background-position:-128px -75px;}
-.xubox_msgtype8{background-position:-91px -6px;}
-.xubox_msgtype9{background-position:-129px -6px;}
-.xubox_msgtype10{background-position:-163px -6px;}
-.xubox_msgtype11{background-position:-206px -6px;}
-.xubox_msgtype12{background-position:-206px -44px;}
-.xubox_msgtype13{background-position:-206px -81px;}
-.xubox_msgtype14{background-position:-206px -122px;}
-.xubox_msgtype15{background-position:-206px -157px;}
-.xubox_loading_0{width:60px; height:24px; background:url(default/xubox_loading0.gif) no-repeat;}
-.xubox_loading_1{width:37px; height:37px; background:url(default/xubox_loading1.gif) no-repeat;}
-.xubox_loading_2, .xubox_msgtype16{width:32px; height:32px; background:url(default/xubox_loading2.gif) no-repeat;}
-.xubox_loading_3{width:126px; height:22px; background:url(default/xubox_loading3.gif) no-repeat;}
-
-.xubox_setwin{position:absolute; right:10px; *right:0; top:10px; font-size:0;}
-.xubox_setwin a{position:relative; display:inline-block; *display:inline; *zoom:1; vertical-align:top; width: 14px; height:14px; margin-left:10px; font-size:12px; _overflow:hidden;}
-.xubox_setwin .xubox_min cite{position:absolute; width:14px; height:2px; left:0; top:50%; margin-top:-1px; background-color:#919191; cursor:pointer; _overflow:hidden;}
-.xubox_setwin .xubox_min:hover cite{background-color:#2D93CA; }
-.xubox_setwin .xubox_max{background-position:-6px -189px;}
-.xubox_setwin .xubox_max:hover{background-position:-6px -206px;}
-.xubox_setwin .xubox_maxmin{background-position:-29px -189px;}
-.xubox_setwin .xubox_maxmin:hover{background-position:-29px -206px;}
-.xubox_setwin .xubox_close0{ width:14px; height:14px; background-position: -31px -7px; cursor:pointer;}
-.xubox_setwin .xubox_close0:hover{background-position:-51px -7px;}
-.xubox_setwin .xubox_close1{position:absolute; right:-28px; top:-28px; width:30px; height:30px; margin-left:0; background-position:-60px -195px; *right:-18px; _right:-15px; _top:-23px; _width:14px; _height:14px; _background-position:-31px -7px;}
-.xubox_setwin .xubox_close1:hover{ background-position:-91px -195px; _background-position:-51px -7px;}
-
-.xubox_title{width:100%; height:35px; line-height:35px; border-bottom:1px solid #D5D5D5; background:url(default/xubox_title0.png) #EBEBEB repeat-x; font-size:14px; color:#333;}
-.xubox_title em{height:20px; line-height:20px; width:60%; top:7px; left:10px; font-style:normal; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;}
-
-.xubox_botton a{position:absolute; bottom:10px; left:50%; background:url(default/xubox_ico0.png) repeat; text-decoration:none; color:#FFF; font-size:14px; text-align:center; font-weight:bold; overflow:hidden; }
-.xubox_botton a:hover{text-decoration:none; color:#FFF; }
-.xubox_botton .xubox_botton1{ width:79px; height:32px; line-height:32px; margin-left:-39px; background-position:-6px -34px;}
-.xubox_botton1:hover{background-position:-6px -72px;}
-.xubox_botton .xubox_botton2{margin-left:-76px; width:71px; height:29px; line-height:29px; background-position:-5px -114px;}
-.xubox_botton2:hover{ background-position:-5px -146px;}
-.xubox_botton .xubox_botton3{width:71px; height:29px; line-height:29px; margin-left:10px; background-position:-81px -114px;}
-.xubox_botton3:hover{background-position:-81px -146px;}
-.xubox_tips{position:relative; line-height:20px; min-width: 12px; padding:3px 30px 3px 10px; font-size:12px; _float:left; border-radius:3px; box-shadow: 1px 1px 3px rgba(0,0,0,.3);}
-.xubox_tips i.layerTipsG{ position:absolute; width:0; height:0; border-width:8px; border-color:transparent; border-style:dashed; *overflow:hidden;}
-.xubox_tips i.layerTipsT, .xubox_tips i.layerTipsB{left:5px; border-right-style:solid;}
-.xubox_tips i.layerTipsT{bottom:-8px;}
-.xubox_tips i.layerTipsB{top:-8px;}
-.xubox_tips i.layerTipsR, .xubox_tips i.layerTipsL{top:1px; border-bottom-style:solid;}
-.xubox_tips i.layerTipsR{left:-8px;}
-.xubox_tips i.layerTipsL{right:-8px;}
-
-
-
+ */*html{background-image:url(about:blank);background-attachment:fixed}html #layui_layer_skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{top:150px;left:50%;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3);border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.3);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-moves{position:absolute;border:3px solid #666;border:3px solid rgba(0,0,0,.5);cursor:move;background-color:#fff;background-color:rgba(255,255,255,.3);filter:alpha(opacity=50)}.layui-layer-load{background:url(default/loading-0.gif) center center no-repeat #fff}.layui-layer-ico{background:url(default/icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);-ms-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layui-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layui-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layui-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px solid #dedede;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.7}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;font-size:14px;overflow:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe .layui-layer-content{overflow:hidden}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(default/loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(default/loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(default/loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.3);background-color:#F90;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#F90}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#F90}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#BBB5B5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}
\ No newline at end of file
diff --git a/static/js/layer/skin/layer.ext.css b/static/js/layer/skin/layer.ext.css
old mode 100644
new mode 100755
index 6570a05d9..7a02607b2
--- a/static/js/layer/skin/layer.ext.css
+++ b/static/js/layer/skin/layer.ext.css
@@ -1,45 +1,8 @@
-/**
+/*!
@Name: layer拓展样式
@Date: 2012.12.13
@Author: 贤心
@blog: sentsin.com
-**/
-
-.xubox_iconext{background:url(default/icon_ext.png) no-repeat;}
-
-/* prompt模式 */
-.xubox_layer .xubox_form{width:240px; height:30px; line-height:30px; padding: 0 5px; border: 1px solid #ccc; background: url(default/textbg.png) #fff repeat-x; color:#333;}
-.xubox_layer .xubox_formArea{width:300px; height:100px; line-height:20px;}
-
-/* tab模式 */
-.xubox_layer .xubox_tab{position:relative; background-color:#fff; box-shadow:1px 1px 50px rgba(0,0,0,.4)}
-.xubox_layer .xubox_tabmove{position:absolute; width:600px; height:30px; top:0; left:0;}
-.xubox_layer .xubox_tabtit{ display:block; height:34px; border-bottom:1px solid #ccc; background-color:#eee;}
-.xubox_layer .xubox_tabtit span{position:relative; float:left; width:120px; height:34px; line-height:34px; text-align:center; cursor:default;}
-.xubox_layer .xubox_tabtit span.xubox_tabnow{left:-1px; _top:1px; height:35px; border-left:1px solid #ccc; border-right:1px solid #ccc; background-color:#fff; z-index:10;}
-.xubox_layer .xubox_tab_main{line-height:24px; clear:both;}
-.xubox_layer .xubox_tab_main .xubox_tabli{display:none;}
-.xubox_layer .xubox_tab_main .xubox_tabli.xubox_tab_layer{display:block;}
-.xubox_layer .xubox_tabclose{position:absolute; right:10px; top:5px; cursor:pointer;}
-
-/* photo模式 */
-.xubox_bigimg, .xubox_intro{height:300px}
-.xubox_bigimg{position:relative; display:block; width:600px; text-align:center; background:url(default/xubox_loading1.gif) center center no-repeat #000; overflow:hidden; }
-.xubox_bigimg img{position:relative; display:inline-block; visibility: hidden;}
-.xubox_intro{position:absolute; right:-315px; top:0; width:300px; background-color:#fff; overflow-x:hidden; overflow-y:auto;}
-.xubox_imgsee{display:none;}
-.xubox_prev, .xubox_next{position:absolute; top:50%; width:27px; _width:44px; height:44px; margin-top:-22px; outline:none;blr:expression(this.onFocus=this.blur());}
-.xubox_prev{left:10px; background-position:-5px -5px; _background-position:-70px -5px;}
-.xubox_prev:hover{background-position:-33px -5px; _background-position:-120px -5px;}
-.xubox_next{right:10px; _right:8px; background-position:-5px -50px; _background-position:-70px -50px;}
-.xubox_next:hover{background-position:-33px -50px; _background-position:-120px -50px;}
-.xubox_imgbar{position:absolute; left:0; bottom:0; width:100%; height:32px; line-height:32px; background-color:rgba(0,0,0,.8); background-color:#000\9; filter:Alpha(opacity=80); color:#fff; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; font-size:0;}
-.xubox_imgtit{/*position:absolute; left:20px;*/}
-.xubox_imgtit *{display:inline-block; *display:inline; *zoom:1; vertical-align:top; font-size:12px;}
-.xubox_imgtit a{max-width:65%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; color:#fff;}
-.xubox_imgtit a:hover{color:#fff; text-decoration:underline;}
-.xubox_imgtit em{padding-left:10px;}
-
-
+ */.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span{text-overflow:ellipsis;white-space:nowrap}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}html #layui_layer_skinlayerextcss{display:none;position:absolute;width:1989px}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s;background:url(default/xubox_loading1.gif) center center no-repeat #000}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}
\ No newline at end of file
diff --git a/templates/foot_script.html b/templates/foot_script.html
index 719670369..c9db331b9 100644
--- a/templates/foot_script.html
+++ b/templates/foot_script.html
@@ -19,7 +19,7 @@
-
+
diff --git a/templates/jasset/asset_add.html b/templates/jasset/asset_add.html
index 1d8eacc28..7ef74920a 100644
--- a/templates/jasset/asset_add.html
+++ b/templates/jasset/asset_add.html
@@ -42,13 +42,11 @@