merge with dev
|
@ -0,0 +1,127 @@
|
||||||
|
快速安装
|
||||||
|
------
|
||||||
|
####环境
|
||||||
|
CentOS 6.x x86_64
|
||||||
|
iptables stop
|
||||||
|
selinux disable
|
||||||
|
|
||||||
|
|
||||||
|
####开始
|
||||||
|
**1. 安装git**
|
||||||
|
|
||||||
|
> yum -y install git
|
||||||
|
|
||||||
|
**2. 下载jumpserver**
|
||||||
|
|
||||||
|
> git clone https://github.com/ibuler/jumpserver.git
|
||||||
|
|
||||||
|
**3. 执行快速安装脚本**
|
||||||
|
|
||||||
|
> cd jumpserver/install && python install.py
|
||||||
|
|
||||||
|
|
||||||
|
*根据提示输入相关信息,完成安装,完成安装后,请访问web,继续查看后续文档*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
名词解释
|
||||||
|
------
|
||||||
|
|
||||||
|
|
||||||
|
* **用户** 用户是授权和登陆的主体,将来为每个员工建立一个账户,用来登录跳板机,
|
||||||
|
将资产授权给该用户,查看用户登陆记录命令历史等
|
||||||
|
|
||||||
|
* **用户组** 多个用户可以组合成用户组,为了方便进行授权,可以将一个部门或几个用户
|
||||||
|
组建成用户组,在授权中使用组授权,该组中的用户拥有所有授权的主机权限
|
||||||
|
|
||||||
|
* **资产** 资产通常是我们的服务器、网络设备等,将资产授权给用户,用户则会有权限登
|
||||||
|
录资产,执行命令等
|
||||||
|
|
||||||
|
* **管理账户** 添加资产时需要添加一个管理账户,该账户是该资产上已有的有管理权限的用户,
|
||||||
|
如root,或者有 NOPASSWD: ALL sudo权限的用户,该管理账户用来向资产推送系统用户,
|
||||||
|
为系统用户添加sudo,获取资产的一些硬件信息
|
||||||
|
|
||||||
|
* **资产组** 同用户组,是资产组成的集合,为了方便授权
|
||||||
|
|
||||||
|
* **机房** 又称IDC,不解释
|
||||||
|
|
||||||
|
* **Sudo** 这里的sudo其实是Linux中的sudo命令别名,一个sudo别名包含多个命令,
|
||||||
|
系统用户关联sudo就代表该系统用户有权限sudo执行这些命令
|
||||||
|
|
||||||
|
* **系统用户** 系统用户是服务器上建立的一些真实存在的可以ssh登陆的用户,如 dev,
|
||||||
|
sa, dba等,系统用户可使用jumpserver推送到服务器上,也可以利用自己公司
|
||||||
|
的工具进行推送,授权时将用户、资产、系统用户关联起来则表明用户有权限登陆该资产的
|
||||||
|
这个系统用户 如:用户 **小明** 以 **dev** 系统用户登陆 **172.16.1.1**资产
|
||||||
|
|
||||||
|
* **授权规则** 授权规则是将 **资产** **系统用户** 和 **用户** 关联起来,用来完成授权。
|
||||||
|
这样用户就可以以某个系统用户账号登陆资产
|
||||||
|
|
||||||
|
* **日志审计**
|
||||||
|
* **在线** 查看当前在线的用户(非web在线),可以监控用户的命令执行,强制结束用户
|
||||||
|
登录。
|
||||||
|
* **登录历史** 查看以往用户的登录历史,可以查看用户登陆操作的命令,可以回放用户
|
||||||
|
执行命令的录像
|
||||||
|
* **命令记录** 查看用户批量执行命令的历史,包含执行命令的主机,执行的命令,执行的结果
|
||||||
|
|
||||||
|
* **上传下载** 查看用户上传下载文件的记录
|
||||||
|
|
||||||
|
|
||||||
|
快速开始
|
||||||
|
------
|
||||||
|
|
||||||
|
##### 1. 添加用户
|
||||||
|
**用户管理 - 查看用户 - 添加用户** 填写基本信息,完成用户添加
|
||||||
|
|
||||||
|
用户添加完成后,根据提示记住用户账号密码,换个浏览器登录下载key,
|
||||||
|
ssh登录jumpserver测试
|
||||||
|
|
||||||
|
##### 2. 添加资产
|
||||||
|
**资产管理 - 查看资产 - 添加资产** 填写基本信息,完成资产添加
|
||||||
|
|
||||||
|
##### 3. 添加sudo
|
||||||
|
**授权管理 - Sudo - 添加别名** 输入别名名称和命令,完成sudo添加
|
||||||
|
|
||||||
|
##### 4. 添加系统用户
|
||||||
|
**授权管理 - 系统用户 - 添加** 输入基本信息,完成系统用户添加
|
||||||
|
|
||||||
|
##### 5. 推送系统用户
|
||||||
|
**授权管理 - 推送** - 选择需要推送的资产或资产组完成推送
|
||||||
|
|
||||||
|
推送只支持服务器,使用密钥是指用户从跳板机跳转时使用key,反之使用密码,
|
||||||
|
授权时会检查推送记录,如果没有推送过则无法完成系统用户在该资产上的授权。
|
||||||
|
如果资产时网络设备,请不要选择密码和秘钥,模拟一下推送,目的是为了生成
|
||||||
|
推送记录。
|
||||||
|
|
||||||
|
##### 6. 添加授权规则
|
||||||
|
**授权管理 - 授权规则 - 添加规则** 选择刚才添加的用户,资产,系统用户完成授权
|
||||||
|
|
||||||
|
##### 7. 测试登录
|
||||||
|
**用户下载key** 登录跳板机,会自动运行connect.py,根据提示登录服务器
|
||||||
|
|
||||||
|
**用户登陆web** 查看授权的主机,点击后面的链接,测试是否可以登录服务器
|
||||||
|
|
||||||
|
##### 8. 监控和结束会话
|
||||||
|
**日志审计 - 在线** 查看当前登录的用户登录情况,点击监控查看用户执行的命令,
|
||||||
|
点击阻断,结束用户的会话
|
||||||
|
|
||||||
|
##### 9. 查看历史记录
|
||||||
|
**日志审计 - 登录历史** 查看登录历史,点击统计查看命令历史,点击回放查看录像
|
||||||
|
|
||||||
|
##### 10. 执行命令
|
||||||
|
同7 测试命令的执行,命令记录查看 批量执行命令的日志
|
||||||
|
|
||||||
|
##### 11. 上传下载
|
||||||
|
同7 测试文件的上传下载,日志审计 - 上传下载 查看上传下载记录
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# coding: utf8
|
||||||
|
|
||||||
|
Jumpserver开发者文档
|
||||||
|
|
||||||
|
开发规范:
|
||||||
|
1. 遵守PE8规范 1) 命名规范 2) 导入模块规范 3) 空行规范 4) 长度规范
|
||||||
|
2. 缩进统一4个空格
|
||||||
|
3. 变量命名明了易懂多个单词下划线隔开
|
||||||
|
4. 注释到位
|
||||||
|
|
||||||
|
|
||||||
|
框架说明:
|
||||||
|
1. 项目名称 Jumpserver
|
||||||
|
2. APP:
|
||||||
|
juser 用户管理
|
||||||
|
jasset 资产管理(设备管理)
|
||||||
|
jpermission 授权管理
|
||||||
|
jlog 日志管理
|
||||||
|
3. connect.py 用户登录入口程序
|
||||||
|
4. logs 日志保存目录
|
||||||
|
5. jumpserver.conf 配置文件
|
||||||
|
6. docs 文档目录
|
||||||
|
7. static 静态文件目录
|
||||||
|
8. templates 模板目录
|
||||||
|
|
||||||
|
|
||||||
|
connect.py逻辑说明:
|
||||||
|
用户登录系统,运行该脚本,p调用get_user_host函数查看有权限的服务器ip
|
||||||
|
输入部分IP,verify_connect匹配该部分ip,如果是匹配到多个,就显示ip
|
||||||
|
匹配到0了就显示没有权限或者主机,
|
||||||
|
匹配到1个则继续
|
||||||
|
查询该服务器是否支持ldap 如果是,获得ldap用户密码登陆
|
||||||
|
如果否,查询授权表,查看该服务器授权的系统用户,并返回对应账号密码,登陆
|
||||||
|
connect函数是登陆函数,采用paramiko 使用channel登陆,posix_shell 来完成交互,并记录日志
|
||||||
|
signal模块来完成窗口改变导致的tty大小随之改变
|
||||||
|
PyCrypt是对称加密类
|
|
@ -0,0 +1,9 @@
|
||||||
|
- model: juser.user
|
||||||
|
pk: 5000
|
||||||
|
fields:
|
||||||
|
username: admin
|
||||||
|
name: admin
|
||||||
|
password: pbkdf2_sha256$20000$jBIDGPB2j5JT$orxqGgzzjzykColYm1BswPjgHOiERjZkcgkuVIkD2Hc=
|
||||||
|
email: admin@jumpserver.org
|
||||||
|
role: SU
|
||||||
|
is_active: 1
|
|
@ -0,0 +1,215 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import smtplib
|
||||||
|
import MySQLdb
|
||||||
|
from smtplib import SMTP, SMTPAuthenticationError, SMTPConnectError
|
||||||
|
import ConfigParser
|
||||||
|
import socket
|
||||||
|
import fcntl
|
||||||
|
import struct
|
||||||
|
|
||||||
|
jms_dir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
|
||||||
|
sys.path.append(jms_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def bash(cmd):
|
||||||
|
"""
|
||||||
|
run a bash shell command
|
||||||
|
执行bash命令
|
||||||
|
"""
|
||||||
|
return subprocess.call(cmd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
def color_print(msg, color='red', exits=False):
|
||||||
|
"""
|
||||||
|
Print colorful string.
|
||||||
|
颜色打印字符或者退出
|
||||||
|
"""
|
||||||
|
color_msg = {'blue': '\033[1;36m%s\033[0m',
|
||||||
|
'green': '\033[1;32m%s\033[0m',
|
||||||
|
'yellow': '\033[1;33m%s\033[0m',
|
||||||
|
'red': '\033[1;31m%s\033[0m',
|
||||||
|
'title': '\033[30;42m%s\033[0m',
|
||||||
|
'info': '\033[32m%s\033[0m'}
|
||||||
|
msg = color_msg.get(color, 'red') % msg
|
||||||
|
print msg
|
||||||
|
if exits:
|
||||||
|
time.sleep(2)
|
||||||
|
sys.exit()
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip_addr(ifname='eth0'):
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
return socket.inet_ntoa(fcntl.ioctl(
|
||||||
|
s.fileno(),
|
||||||
|
0x8915,
|
||||||
|
struct.pack('256s', ifname[:15])
|
||||||
|
)[20:24])
|
||||||
|
except:
|
||||||
|
ips = os.popen("LANG=C ifconfig | grep \"inet addr\" | grep -v \"127.0.0.1\" | awk -F \":\" '{print $2}' | awk '{print $1}'").readlines()
|
||||||
|
if len(ips) > 0:
|
||||||
|
return ips[0]
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class PreSetup(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.db_host = '127.0.0.1'
|
||||||
|
self.db_port = 3306
|
||||||
|
self.db_user = 'jumpserver'
|
||||||
|
self.db_pass = 'mysql234'
|
||||||
|
self.db = 'jumpserver'
|
||||||
|
self.mail_host = 'smtp.qq.com'
|
||||||
|
self.mail_port = 25
|
||||||
|
self.mail_addr = 'hello@jumpserver.org'
|
||||||
|
self.mail_pass = ''
|
||||||
|
self.ip = ''
|
||||||
|
|
||||||
|
def write_conf(self, conf_file=os.path.join(jms_dir, 'jumpserver.conf')):
|
||||||
|
color_print('开始写入配置文件', 'green')
|
||||||
|
conf = ConfigParser.ConfigParser()
|
||||||
|
conf.read(conf_file)
|
||||||
|
conf.set('base', 'url', 'http://%s' % self.ip)
|
||||||
|
conf.set('db', 'host', self.db_host)
|
||||||
|
conf.set('db', 'port', self.db_port)
|
||||||
|
conf.set('db', 'user', self.db_user)
|
||||||
|
conf.set('db', 'password', self.db_pass)
|
||||||
|
conf.set('db', 'database', self.db)
|
||||||
|
conf.set('websocket', 'web_socket_host', '%s:3000' % self.ip)
|
||||||
|
conf.set('mail', 'email_host', self.mail_host)
|
||||||
|
conf.set('mail', 'email_port', self.mail_port)
|
||||||
|
conf.set('mail', 'email_host_user', self.mail_addr)
|
||||||
|
conf.set('mail', 'email_host_password', self.mail_pass)
|
||||||
|
|
||||||
|
with open(conf_file, 'w') as f:
|
||||||
|
conf.write(f)
|
||||||
|
|
||||||
|
def _setup_mysql(self):
|
||||||
|
color_print('开始安装设置mysql (请手动设置mysql安全)', 'green')
|
||||||
|
bash('yum -y install mysql-server')
|
||||||
|
bash('service mysqld start')
|
||||||
|
bash('mysql -e "create database %s default charset=utf8"' % self.db)
|
||||||
|
bash('mysql -e "grant all on %s.* to \'%s\'@\'%s\' identified by \'%s\'"' % (self.db,
|
||||||
|
self.db_user,
|
||||||
|
self.db_host,
|
||||||
|
self.db_pass))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _set_env():
|
||||||
|
color_print('开始关闭防火墙和selinux', 'green')
|
||||||
|
bash('service iptables stop && chkconfig iptables off && setenforce 0')
|
||||||
|
|
||||||
|
def _test_db_conn(self):
|
||||||
|
try:
|
||||||
|
MySQLdb.connect(host=self.db_host, port=self.db_port,
|
||||||
|
user=self.db_user, passwd=self.db_pass, db=self.db)
|
||||||
|
color_print('连接数据库成功', 'green')
|
||||||
|
return True
|
||||||
|
except MySQLdb.OperationalError, e:
|
||||||
|
color_print('数据库连接失败 %s' % e, 'red')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _test_mail(self):
|
||||||
|
try:
|
||||||
|
smtp = SMTP(self.mail_host, port=self.mail_port, timeout=2)
|
||||||
|
smtp.login(self.mail_addr, self.mail_pass)
|
||||||
|
smtp.sendmail(self.mail_addr, (self.mail_addr, ),
|
||||||
|
'''From:%s\r\nTo:%s\r\nSubject:Jumpserver Mail Test!\r\n\r\n Mail test passed!\r\n''' %
|
||||||
|
(self.mail_addr, self.mail_addr))
|
||||||
|
smtp.quit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except (SMTPAuthenticationError, socket.timeout, socket.gaierror), e:
|
||||||
|
color_print(e, 'red')
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _rpm_repo():
|
||||||
|
color_print('开始安装epel源', 'green')
|
||||||
|
bash('yum -y install epel-release')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _depend_rpm():
|
||||||
|
color_print('开始安装依赖rpm包', 'green')
|
||||||
|
bash('yum -y install git python-pip mysql-devel gcc automake autoconf python-devel vim sshpass')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _require_pip():
|
||||||
|
color_print('开始安装依赖pip包', 'green')
|
||||||
|
bash('pip install -r requirements.txt')
|
||||||
|
|
||||||
|
def _input_ip(self):
|
||||||
|
ip = raw_input('\n请输入您服务器的IP地址,用户浏览器可以访问 [%s]: ' % get_ip_addr())
|
||||||
|
self.ip = ip if ip else get_ip_addr()
|
||||||
|
|
||||||
|
def _input_mysql(self):
|
||||||
|
while True:
|
||||||
|
db_host = raw_input('请输入数据库服务器IP [127.0.0.1]: ')
|
||||||
|
db_port = raw_input('请输入数据库服务器端口 [3306]: ')
|
||||||
|
db_user = raw_input('请输入数据库服务器用户 [root]: ')
|
||||||
|
db_pass = raw_input('请输入数据库服务器密码: ')
|
||||||
|
db = raw_input('请输入使用的数据库 [jumpserver]: ')
|
||||||
|
|
||||||
|
if db_host: self.db_host = db_host
|
||||||
|
if db_port: self.db_port = db_port
|
||||||
|
if db_user: self.db_user = db_user
|
||||||
|
if db_pass: self.db_pass = db_pass
|
||||||
|
if db: self.db = db
|
||||||
|
|
||||||
|
mysql = raw_input('是否使用已经存在的数据库服务器? (y/n) [n]: ')
|
||||||
|
|
||||||
|
if mysql != 'y':
|
||||||
|
self._setup_mysql()
|
||||||
|
|
||||||
|
if self._test_db_conn():
|
||||||
|
break
|
||||||
|
|
||||||
|
print
|
||||||
|
|
||||||
|
def _input_smtp(self):
|
||||||
|
while True:
|
||||||
|
self.mail_host = raw_input('请输入SMTP地址: ').strip()
|
||||||
|
self.mail_port = int(raw_input('请输入SMTP端口: ').strip())
|
||||||
|
self.mail_addr = raw_input('请输入账户: ').strip()
|
||||||
|
self.mail_pass = raw_input('请输入密码: ').strip()
|
||||||
|
|
||||||
|
if self._test_mail():
|
||||||
|
color_print('\n\t请登陆邮箱查收邮件, 然后确认是否继续安装\n', 'green')
|
||||||
|
smtp = raw_input('是否继续? (y/n) [y]: ')
|
||||||
|
if smtp == 'n':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
print
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _pull():
|
||||||
|
color_print('开始更新jumpserver', 'green')
|
||||||
|
bash('git pull')
|
||||||
|
os.chdir(jms_dir)
|
||||||
|
os.chmod('logs', 0777)
|
||||||
|
os.chmod('keys', 0777)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self._rpm_repo()
|
||||||
|
self._depend_rpm()
|
||||||
|
self._require_pip()
|
||||||
|
self._set_env()
|
||||||
|
self._pull()
|
||||||
|
self._input_ip()
|
||||||
|
self._input_mysql()
|
||||||
|
self._input_smtp()
|
||||||
|
self.write_conf()
|
||||||
|
os.system('python %s' % os.path.join(jms_dir, 'install/next.py'))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pre_setup = PreSetup()
|
||||||
|
pre_setup.start()
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import django
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
jms_dir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
|
||||||
|
sys.path.append(jms_dir)
|
||||||
|
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
|
||||||
|
if django.get_version() != '1.6':
|
||||||
|
setup = django.setup()
|
||||||
|
|
||||||
|
from juser.user_api import db_add_user, get_object, User
|
||||||
|
from install import color_print
|
||||||
|
|
||||||
|
|
||||||
|
class Setup(object):
|
||||||
|
"""
|
||||||
|
安装jumpserver向导
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.admin_user = 'admin'
|
||||||
|
self.admin_pass = '5Lov@wife'
|
||||||
|
|
||||||
|
def _input_admin(self):
|
||||||
|
while True:
|
||||||
|
admin_user = raw_input('请输入管理员用户名 [%s]: ' % self.admin_user).strip()
|
||||||
|
admin_pass = raw_input('请输入管理员密码: [%s]: ' % self.admin_pass).strip()
|
||||||
|
admin_pass_again = raw_input('请再次输入管理员密码: [%s]: ' % self.admin_pass).strip()
|
||||||
|
|
||||||
|
if admin_user:
|
||||||
|
self.admin_user = admin_user
|
||||||
|
|
||||||
|
if not admin_pass_again:
|
||||||
|
admin_pass_again = self.admin_pass
|
||||||
|
|
||||||
|
if admin_pass:
|
||||||
|
self.admin_pass = admin_pass
|
||||||
|
|
||||||
|
if self.admin_pass != admin_pass_again:
|
||||||
|
color_print('两次密码不相同请重新输入')
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
print
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sync_db():
|
||||||
|
os.chdir(jms_dir)
|
||||||
|
execute_from_command_line(['manage.py', 'syncdb', '--noinput'])
|
||||||
|
|
||||||
|
def _create_admin(self):
|
||||||
|
user = get_object(User, username=self.admin_user)
|
||||||
|
if user:
|
||||||
|
user.delete()
|
||||||
|
db_add_user(username=self.admin_user, password=self.admin_pass, role='SU', name='admin', groups='',
|
||||||
|
admin_groups='', email='admin@jumpserver.org', uuid='MayBeYouAreTheFirstUser', is_active=True)
|
||||||
|
os.system('id %s &> /dev/null || useradd %s' % (self.admin_user, self.admin_user))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _cp_zzsh():
|
||||||
|
os.chdir(os.path.join(jms_dir, 'install'))
|
||||||
|
shutil.copy('zzjumpserver.sh', '/etc/profile.d/')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _run_service():
|
||||||
|
os.system('sh %s start' % os.path.join(jms_dir, 'service.sh'))
|
||||||
|
print
|
||||||
|
color_print('安装成功,请访问web, 祝你使用愉快。请访问 https://github.com/ibuler/jumpserver 查看文档')
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
print "开始安装Jumpserver, 要求环境为 CentOS 6.5 x86_64"
|
||||||
|
self._sync_db()
|
||||||
|
self._input_admin()
|
||||||
|
self._create_admin()
|
||||||
|
self._cp_zzsh()
|
||||||
|
self._run_service()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setup = Setup()
|
||||||
|
setup.start()
|
|
@ -0,0 +1,17 @@
|
||||||
|
#sphinx-me==0.3
|
||||||
|
django==1.6
|
||||||
|
pycrypto==2.6.1
|
||||||
|
paramiko==1.16.0
|
||||||
|
ecdsa==0.13
|
||||||
|
MySQL-python==1.2.5
|
||||||
|
#django-uuidfield==0.5.0
|
||||||
|
psutil==3.3.0
|
||||||
|
xlsxwriter==0.7.7
|
||||||
|
xlrd==0.9.4
|
||||||
|
django-bootstrap-form==3.2
|
||||||
|
tornado==4.3
|
||||||
|
ansible==1.9.4
|
||||||
|
pyinotify==0.9.6
|
||||||
|
passlib==1.6.5
|
||||||
|
argparse==1.4.0
|
||||||
|
django_crontab==0.6.0
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$USER" == "admin" ] || [ "$USER" == "root" ] || [ "$USER" == "" ];then
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
python /opt/jumpserver/connect.py
|
||||||
|
if [ $USER == 'guanghongwei' ];then
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
exit 3
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,388 @@
|
||||||
|
# coding: utf-8
|
||||||
|
from __future__ import division
|
||||||
|
import xlrd
|
||||||
|
import xlsxwriter
|
||||||
|
from django.db.models import AutoField
|
||||||
|
from jumpserver.api import *
|
||||||
|
from jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord
|
||||||
|
from jperm.ansible_api import MyRunner
|
||||||
|
from jperm.perm_api import gen_resource
|
||||||
|
from jumpserver.templatetags.mytags import get_disk_info
|
||||||
|
|
||||||
|
|
||||||
|
def group_add_asset(group, asset_id=None, asset_ip=None):
|
||||||
|
"""
|
||||||
|
资产组添加资产
|
||||||
|
Asset group add a asset
|
||||||
|
"""
|
||||||
|
if asset_id:
|
||||||
|
asset = get_object(Asset, id=asset_id)
|
||||||
|
else:
|
||||||
|
asset = get_object(Asset, ip=asset_ip)
|
||||||
|
|
||||||
|
if asset:
|
||||||
|
group.asset_set.add(asset)
|
||||||
|
|
||||||
|
|
||||||
|
def db_add_group(**kwargs):
|
||||||
|
"""
|
||||||
|
add a asset group in database
|
||||||
|
数据库中添加资产
|
||||||
|
"""
|
||||||
|
name = kwargs.get('name')
|
||||||
|
group = get_object(AssetGroup, name=name)
|
||||||
|
asset_id_list = kwargs.pop('asset_select')
|
||||||
|
|
||||||
|
if not group:
|
||||||
|
group = AssetGroup(**kwargs)
|
||||||
|
group.save()
|
||||||
|
for asset_id in asset_id_list:
|
||||||
|
group_add_asset(group, asset_id)
|
||||||
|
|
||||||
|
|
||||||
|
def db_update_group(**kwargs):
|
||||||
|
"""
|
||||||
|
add a asset group in database
|
||||||
|
数据库中更新资产
|
||||||
|
"""
|
||||||
|
group_id = kwargs.pop('id')
|
||||||
|
asset_id_list = kwargs.pop('asset_select')
|
||||||
|
group = get_object(AssetGroup, id=group_id)
|
||||||
|
|
||||||
|
for asset_id in asset_id_list:
|
||||||
|
group_add_asset(group, asset_id)
|
||||||
|
|
||||||
|
AssetGroup.objects.filter(id=group_id).update(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def db_asset_add(**kwargs):
|
||||||
|
"""
|
||||||
|
add asset to db
|
||||||
|
添加主机时数据库操作函数
|
||||||
|
"""
|
||||||
|
group_id_list = kwargs.pop('groups')
|
||||||
|
asset = Asset(**kwargs)
|
||||||
|
asset.save()
|
||||||
|
|
||||||
|
group_select = []
|
||||||
|
for group_id in group_id_list:
|
||||||
|
group = AssetGroup.objects.filter(id=group_id)
|
||||||
|
group_select.extend(group)
|
||||||
|
asset.group = group_select
|
||||||
|
|
||||||
|
|
||||||
|
def db_asset_update(**kwargs):
|
||||||
|
""" 修改主机时数据库操作函数 """
|
||||||
|
asset_id = kwargs.pop('id')
|
||||||
|
Asset.objects.filter(id=asset_id).update(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def sort_ip_list(ip_list):
|
||||||
|
""" ip地址排序 """
|
||||||
|
ip_list.sort(key=lambda s: map(int, s.split('.')))
|
||||||
|
return ip_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_tuple_name(asset_tuple, value):
|
||||||
|
""""""
|
||||||
|
for t in asset_tuple:
|
||||||
|
if t[0] == value:
|
||||||
|
return t[1]
|
||||||
|
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def get_tuple_diff(asset_tuple, field_name, value):
|
||||||
|
""""""
|
||||||
|
old_name = get_tuple_name(asset_tuple, int(value[0])) if value[0] else u''
|
||||||
|
new_name = get_tuple_name(asset_tuple, int(value[1])) if value[1] else u''
|
||||||
|
alert_info = [field_name, old_name, new_name]
|
||||||
|
return alert_info
|
||||||
|
|
||||||
|
|
||||||
|
def asset_diff(before, after):
|
||||||
|
"""
|
||||||
|
asset change before and after
|
||||||
|
"""
|
||||||
|
alter_dic = {}
|
||||||
|
before_dic, after_dic = before, dict(after.iterlists())
|
||||||
|
for k, v in before_dic.items():
|
||||||
|
after_dic_values = after_dic.get(k, [])
|
||||||
|
if k == 'group':
|
||||||
|
after_dic_value = after_dic_values if len(after_dic_values) > 0 else u''
|
||||||
|
uv = v if v is not None else u''
|
||||||
|
else:
|
||||||
|
after_dic_value = after_dic_values[0] if len(after_dic_values) > 0 else u''
|
||||||
|
uv = unicode(v) if v is not None else u''
|
||||||
|
if uv != after_dic_value:
|
||||||
|
alter_dic.update({k: [uv, after_dic_value]})
|
||||||
|
|
||||||
|
for k, v in alter_dic.items():
|
||||||
|
if v == [None, u'']:
|
||||||
|
alter_dic.pop(k)
|
||||||
|
|
||||||
|
return alter_dic
|
||||||
|
|
||||||
|
|
||||||
|
def asset_diff_one(before, after):
|
||||||
|
print before.__dict__, after.__dict__
|
||||||
|
fields = Asset._meta.get_all_field_names()
|
||||||
|
for field in fields:
|
||||||
|
print before.field, after.field
|
||||||
|
|
||||||
|
|
||||||
|
def db_asset_alert(asset, username, alert_dic):
|
||||||
|
"""
|
||||||
|
asset alert info to db
|
||||||
|
"""
|
||||||
|
alert_list = []
|
||||||
|
asset_tuple_dic = {'status': ASSET_STATUS, 'env': ASSET_ENV, 'asset_type': ASSET_TYPE}
|
||||||
|
for field, value in alert_dic.iteritems():
|
||||||
|
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''
|
||||||
|
new = IDC.objects.filter(id=value[1]) if value[1] else u''
|
||||||
|
old_name = old[0].name if old else u''
|
||||||
|
new_name = new[0].name if new else u''
|
||||||
|
alert_info = [field_name, old_name, new_name]
|
||||||
|
|
||||||
|
elif field in ['status', 'env', 'asset_type']:
|
||||||
|
alert_info = get_tuple_diff(asset_tuple_dic.get(field), field_name, value)
|
||||||
|
|
||||||
|
elif field == 'group':
|
||||||
|
old, new = [], []
|
||||||
|
for group_id in value[0]:
|
||||||
|
group_name = AssetGroup.objects.get(id=int(group_id)).name
|
||||||
|
old.append(group_name)
|
||||||
|
for group_id in value[1]:
|
||||||
|
group_name = AssetGroup.objects.get(id=int(group_id)).name
|
||||||
|
new.append(group_name)
|
||||||
|
if sorted(old) == sorted(new):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
alert_info = [field_name, ','.join(old), ','.join(new)]
|
||||||
|
|
||||||
|
elif field == 'use_default_auth':
|
||||||
|
if unicode(value[0]) == 'True' and unicode(value[1]) == 'on' or \
|
||||||
|
unicode(value[0]) == 'False' and unicode(value[1]) == '':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
name = asset.username
|
||||||
|
alert_info = [field_name, u'默认', name] if unicode(value[0]) == 'True' else \
|
||||||
|
[field_name, name, u'默认']
|
||||||
|
|
||||||
|
elif field in ['username', 'password']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif field == 'is_active':
|
||||||
|
if unicode(value[0]) == 'True' and unicode(value[1]) == '1' or \
|
||||||
|
unicode(value[0]) == 'False' and unicode(value[1]) == '0':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
alert_info = [u'是否激活', u'激活', u'禁用'] if unicode(value[0]) == 'True' else \
|
||||||
|
[u'是否激活', u'禁用', u'激活']
|
||||||
|
|
||||||
|
else:
|
||||||
|
alert_info = [field_name, unicode(value[0]), unicode(value[1])]
|
||||||
|
|
||||||
|
if 'alert_info' in dir():
|
||||||
|
alert_list.append(alert_info)
|
||||||
|
|
||||||
|
if alert_list:
|
||||||
|
AssetRecord.objects.create(asset=asset, username=username, content=alert_list)
|
||||||
|
|
||||||
|
|
||||||
|
def write_excel(asset_all):
|
||||||
|
data = []
|
||||||
|
now = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M')
|
||||||
|
file_name = 'cmdb_excel_' + now + '.xlsx'
|
||||||
|
workbook = xlsxwriter.Workbook('static/files/excels/%s' % file_name)
|
||||||
|
worksheet = workbook.add_worksheet(u'CMDB数据')
|
||||||
|
worksheet.set_first_sheet()
|
||||||
|
worksheet.set_column('A:E', 15)
|
||||||
|
worksheet.set_column('F:F', 40)
|
||||||
|
worksheet.set_column('G:Z', 15)
|
||||||
|
title = [u'主机名', u'IP', u'IDC', u'所属主机组', u'操作系统', u'CPU', u'内存(G)', u'硬盘(G)',
|
||||||
|
u'机柜位置', u'MAC', u'远控IP', u'机器状态', u'备注']
|
||||||
|
for asset in asset_all:
|
||||||
|
group_list = []
|
||||||
|
for p in asset.group.all():
|
||||||
|
group_list.append(p.name)
|
||||||
|
|
||||||
|
disk = get_disk_info(asset.disk)
|
||||||
|
group_all = '/'.join(group_list)
|
||||||
|
status = asset.get_status_display()
|
||||||
|
idc_name = asset.idc.name if asset.idc else u''
|
||||||
|
system_type = asset.system_type if asset.system_type else u''
|
||||||
|
system_version = asset.system_version if asset.system_version else u''
|
||||||
|
system_os = unicode(system_type) + unicode(system_version)
|
||||||
|
|
||||||
|
alter_dic = [asset.hostname, asset.ip, idc_name, group_all, system_os, asset.cpu, asset.memory,
|
||||||
|
disk, asset.cabinet, asset.mac, asset.remote_ip, status, asset.comment]
|
||||||
|
data.append(alter_dic)
|
||||||
|
format = workbook.add_format()
|
||||||
|
format.set_border(1)
|
||||||
|
format.set_align('center')
|
||||||
|
format.set_align('vcenter')
|
||||||
|
format.set_text_wrap()
|
||||||
|
|
||||||
|
format_title = workbook.add_format()
|
||||||
|
format_title.set_border(1)
|
||||||
|
format_title.set_bg_color('#cccccc')
|
||||||
|
format_title.set_align('center')
|
||||||
|
format_title.set_bold()
|
||||||
|
|
||||||
|
format_ave = workbook.add_format()
|
||||||
|
format_ave.set_border(1)
|
||||||
|
format_ave.set_num_format('0.00')
|
||||||
|
|
||||||
|
worksheet.write_row('A1', title, format_title)
|
||||||
|
i = 2
|
||||||
|
for alter_dic in data:
|
||||||
|
location = 'A' + str(i)
|
||||||
|
worksheet.write_row(location, alter_dic, format)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
workbook.close()
|
||||||
|
ret = (True, file_name)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def copy_model_instance(obj):
|
||||||
|
initial = dict([(f.name, getattr(obj, f.name))
|
||||||
|
for f in obj._meta.fields
|
||||||
|
if not isinstance(f, AutoField) and \
|
||||||
|
not f in obj._meta.parents.values()])
|
||||||
|
return obj.__class__(**initial)
|
||||||
|
|
||||||
|
|
||||||
|
def ansible_record(asset, ansible_dic, username):
|
||||||
|
alert_dic = {}
|
||||||
|
asset_dic = asset.__dict__
|
||||||
|
for field, value in ansible_dic.items():
|
||||||
|
old = asset_dic.get(field)
|
||||||
|
new = ansible_dic.get(field)
|
||||||
|
if unicode(old) != unicode(new):
|
||||||
|
setattr(asset, field, value)
|
||||||
|
asset.save()
|
||||||
|
alert_dic[field] = [old, new]
|
||||||
|
|
||||||
|
db_asset_alert(asset, username, alert_dic)
|
||||||
|
|
||||||
|
|
||||||
|
def excel_to_db(excel_file):
|
||||||
|
"""
|
||||||
|
Asset add batch function
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = xlrd.open_workbook(filename=None, file_contents=excel_file.read())
|
||||||
|
except Exception, e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
table = data.sheets()[0]
|
||||||
|
rows = table.nrows
|
||||||
|
group_instance = []
|
||||||
|
for row_num in range(1, rows):
|
||||||
|
row = table.row_values(row_num)
|
||||||
|
if row:
|
||||||
|
ip, port, hostname, use_default_auth, username, password, group = row
|
||||||
|
if get_object(Asset, hostname=hostname):
|
||||||
|
continue
|
||||||
|
use_default_auth = 1 if use_default_auth == u'默认' else 0
|
||||||
|
password_encode = CRYPTOR.encrypt(password) if password else ''
|
||||||
|
if hostname:
|
||||||
|
asset = Asset(ip=ip,
|
||||||
|
port=port,
|
||||||
|
hostname=hostname,
|
||||||
|
use_default_auth=use_default_auth,
|
||||||
|
username=username,
|
||||||
|
password=password_encode
|
||||||
|
)
|
||||||
|
asset.save()
|
||||||
|
group_list = group.split('/')
|
||||||
|
for group_name in group_list:
|
||||||
|
group = get_object(AssetGroup, name=group_name)
|
||||||
|
if group:
|
||||||
|
group_instance.append(group)
|
||||||
|
if group_instance:
|
||||||
|
print group_instance
|
||||||
|
asset.group = group_instance
|
||||||
|
asset.save()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_ansible_asset_info(asset_ip, setup_info):
|
||||||
|
print asset_ip
|
||||||
|
disk_need = {}
|
||||||
|
disk_all = setup_info.get("ansible_devices")
|
||||||
|
if disk_all:
|
||||||
|
for disk_name, disk_info in disk_all.iteritems():
|
||||||
|
print disk_name, disk_info
|
||||||
|
if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'):
|
||||||
|
disk_size = disk_info.get("size", '')
|
||||||
|
if 'M' in disk_size:
|
||||||
|
disk_format = round(float(disk_size[:-2]) / 1000, 0)
|
||||||
|
elif 'T' in disk_size:
|
||||||
|
disk_format = round(float(disk_size[:-2]) * 1000, 0)
|
||||||
|
else:
|
||||||
|
disk_format = float(disk_size[:-2])
|
||||||
|
disk_need[disk_name] = disk_format
|
||||||
|
all_ip = setup_info.get("ansible_all_ipv4_addresses")
|
||||||
|
other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else []
|
||||||
|
other_ip = ','.join(other_ip_list) if other_ip_list else ''
|
||||||
|
# hostname = setup_info.get("ansible_hostname")
|
||||||
|
# ip = setup_info.get("ansible_default_ipv4").get("address")
|
||||||
|
mac = setup_info.get("ansible_default_ipv4").get("macaddress")
|
||||||
|
brand = setup_info.get("ansible_product_name")
|
||||||
|
cpu_type = setup_info.get("ansible_processor")[1]
|
||||||
|
cpu_cores = setup_info.get("ansible_processor_count")
|
||||||
|
cpu = cpu_type + ' * ' + unicode(cpu_cores)
|
||||||
|
memory = setup_info.get("ansible_memtotal_mb")
|
||||||
|
try:
|
||||||
|
memory_format = int(round((int(memory) / 1000), 0))
|
||||||
|
except Exception:
|
||||||
|
memory_format = memory
|
||||||
|
disk = disk_need
|
||||||
|
system_type = setup_info.get("ansible_distribution")
|
||||||
|
system_version = setup_info.get("ansible_distribution_version")
|
||||||
|
system_arch = setup_info.get("ansible_architecture")
|
||||||
|
# asset_type = setup_info.get("ansible_system")
|
||||||
|
sn = setup_info.get("ansible_product_serial")
|
||||||
|
asset_info = [other_ip, mac, cpu, memory_format, disk, sn, system_type, system_version, brand, system_arch]
|
||||||
|
return asset_info
|
||||||
|
|
||||||
|
|
||||||
|
def asset_ansible_update(obj_list, name=''):
|
||||||
|
resource = gen_resource(obj_list)
|
||||||
|
ansible_instance = MyRunner(resource)
|
||||||
|
ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*')
|
||||||
|
logger.debug('获取硬件信息: %s' % ansible_asset_info)
|
||||||
|
for asset in obj_list:
|
||||||
|
try:
|
||||||
|
setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts']
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
asset_info = get_ansible_asset_info(asset.ip, setup_info)
|
||||||
|
other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand, system_arch = asset_info
|
||||||
|
asset_dic = {"other_ip": other_ip,
|
||||||
|
"mac": mac,
|
||||||
|
"cpu": cpu,
|
||||||
|
"memory": memory,
|
||||||
|
"disk": disk,
|
||||||
|
"sn": sn,
|
||||||
|
"system_type": system_type,
|
||||||
|
"system_version": system_version,
|
||||||
|
"system_arch": system_arch,
|
||||||
|
"brand": brand
|
||||||
|
}
|
||||||
|
|
||||||
|
ansible_record(asset, asset_dic, name)
|
||||||
|
|
||||||
|
|
||||||
|
def asset_ansible_update_all():
|
||||||
|
name = u'定时更新'
|
||||||
|
asset_all = Asset.objects.all()
|
||||||
|
asset_ansible_update(asset_all, name)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# coding:utf-8
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from jasset.models import IDC, Asset, AssetGroup
|
||||||
|
|
||||||
|
|
||||||
|
class AssetForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Asset
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
"ip", "other_ip", "hostname", "port", "group", "username", "password", "use_default_auth",
|
||||||
|
"idc", "mac", "remote_ip", "brand", "cpu", "memory", "disk", "system_type", "system_version",
|
||||||
|
"cabinet", "position", "number", "status", "asset_type", "env", "sn", "is_active", "comment",
|
||||||
|
"system_arch"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = AssetGroup
|
||||||
|
fields = [
|
||||||
|
"name", "comment"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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'})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
|
||||||
|
from argparse import ArgumentParser, FileType
|
||||||
|
from contextlib import closing
|
||||||
|
from io import open as copen
|
||||||
|
from json import dumps
|
||||||
|
from math import ceil
|
||||||
|
import re
|
||||||
|
from os.path import basename, dirname, exists, join
|
||||||
|
from struct import unpack
|
||||||
|
from subprocess import Popen
|
||||||
|
from sys import platform, prefix, stderr
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
from jinja2 import FileSystemLoader, Template
|
||||||
|
from jinja2.environment import Environment
|
||||||
|
|
||||||
|
from jumpserver.api import BASE_DIR
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_TEMPLATE = join(BASE_DIR, 'templates', 'jlog', 'static.jinja2')
|
||||||
|
rz_pat = re.compile(r'\x18B\w+\r\x8a(\x11)?')
|
||||||
|
|
||||||
|
|
||||||
|
def escapeString(string):
|
||||||
|
string = rz_pat.sub('', string)
|
||||||
|
try:
|
||||||
|
string = string.encode('unicode_escape').decode('utf-8', 'ignore')
|
||||||
|
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||||
|
string = string.decode('utf-8', 'ignore')
|
||||||
|
string = string.replace("'", "\\'")
|
||||||
|
string = '\'' + string + '\''
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def getTiming(timef):
|
||||||
|
timing = None
|
||||||
|
with closing(timef):
|
||||||
|
timing = [l.strip().split(' ') for l in timef]
|
||||||
|
timing = [(int(ceil(float(r[0]) * 1000)), int(r[1])) for r in timing]
|
||||||
|
return timing
|
||||||
|
|
||||||
|
|
||||||
|
def scriptToJSON(scriptf, timing=None):
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
with closing(scriptf):
|
||||||
|
scriptf.readline() # ignore first header line from script file
|
||||||
|
offset = 0
|
||||||
|
for t in timing:
|
||||||
|
dt = scriptf.read(t[1])
|
||||||
|
data = escapeString(dt)
|
||||||
|
# print ('###### (%s, %s)' % (t[1], repr(data)))
|
||||||
|
offset += t[0]
|
||||||
|
ret.append((data, offset))
|
||||||
|
return dumps(ret)
|
||||||
|
|
||||||
|
|
||||||
|
def renderTemplate(script_path, time_file_path, dimensions=(24, 80), templatename=DEFAULT_TEMPLATE):
|
||||||
|
with copen(script_path, encoding='utf-8', errors='replace', newline='\r\n') as scriptf:
|
||||||
|
# with open(script_path) as scriptf:
|
||||||
|
with open(time_file_path) as timef:
|
||||||
|
timing = getTiming(timef)
|
||||||
|
json = scriptToJSON(scriptf, timing)
|
||||||
|
|
||||||
|
fsl = FileSystemLoader(dirname(templatename), 'utf-8')
|
||||||
|
e = Environment()
|
||||||
|
e.loader = fsl
|
||||||
|
|
||||||
|
templatename = basename(templatename)
|
||||||
|
rendered = e.get_template(templatename).render(json=json,
|
||||||
|
dimensions=dimensions)
|
||||||
|
|
||||||
|
return rendered
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Jperm App
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 模块 ansible_api
|
||||||
|
|
||||||
|
> 使用说明
|
||||||
|
|
||||||
|
+ 依赖rpm安装包: ansible、 sshpass
|
||||||
|
+ 依赖pip安装包: passlib
|
||||||
|
+ 关于ansible配置: 需要启用配置文件(/etc/ansible/ansible.cfg)的 host_key_checking = False
|
||||||
|
|
|
@ -0,0 +1,506 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from ansible.inventory.group import Group
|
||||||
|
from ansible.inventory.host import Host
|
||||||
|
from ansible.inventory import Inventory
|
||||||
|
from ansible.runner import Runner
|
||||||
|
from ansible.playbook import PlayBook
|
||||||
|
from ansible import callbacks
|
||||||
|
from ansible import utils
|
||||||
|
import ansible.constants as C
|
||||||
|
from passlib.hash import sha512_crypt
|
||||||
|
from django.template.loader import get_template
|
||||||
|
from django.template import Context
|
||||||
|
|
||||||
|
|
||||||
|
from jumpserver.api import logger
|
||||||
|
|
||||||
|
|
||||||
|
API_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks')
|
||||||
|
C.HOST_KEY_CHECKING = False
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleError(StandardError):
|
||||||
|
"""
|
||||||
|
the base AnsibleError which contains error(required),
|
||||||
|
data(optional) and message(optional).
|
||||||
|
存储所有Ansible 异常对象
|
||||||
|
"""
|
||||||
|
def __init__(self, error, data='', message=''):
|
||||||
|
super(AnsibleError, self).__init__(message)
|
||||||
|
self.error = error
|
||||||
|
self.data = data
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
|
class CommandValueError(AnsibleError):
|
||||||
|
"""
|
||||||
|
indicate the input value has error or invalid.
|
||||||
|
the data specifies the error field of input form.
|
||||||
|
输入不合法 异常对象
|
||||||
|
"""
|
||||||
|
def __init__(self, field, message=''):
|
||||||
|
super(CommandValueError, self).__init__('value:invalid', field, message)
|
||||||
|
|
||||||
|
|
||||||
|
class MyInventory(Inventory):
|
||||||
|
"""
|
||||||
|
this is my ansible inventory object.
|
||||||
|
"""
|
||||||
|
def __init__(self, resource):
|
||||||
|
"""
|
||||||
|
resource的数据格式是一个列表字典,比如
|
||||||
|
{
|
||||||
|
"group1": {
|
||||||
|
"hosts": [{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...],
|
||||||
|
"vars": {"var1": value1, "var2": value2, ...}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
如果你只传入1个列表,这默认该列表内的所有主机属于my_group组,比如
|
||||||
|
[{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...]
|
||||||
|
"""
|
||||||
|
self.resource = resource
|
||||||
|
self.inventory = Inventory(host_list=[])
|
||||||
|
self.gen_inventory()
|
||||||
|
|
||||||
|
def my_add_group(self, hosts, groupname, groupvars=None):
|
||||||
|
"""
|
||||||
|
add hosts to a group
|
||||||
|
"""
|
||||||
|
my_group = Group(name=groupname)
|
||||||
|
|
||||||
|
# if group variables exists, add them to group
|
||||||
|
if groupvars:
|
||||||
|
for key, value in groupvars.iteritems():
|
||||||
|
my_group.set_variable(key, value)
|
||||||
|
|
||||||
|
# add hosts to group
|
||||||
|
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', 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"]:
|
||||||
|
my_host.set_variable(key, value)
|
||||||
|
# add to group
|
||||||
|
my_group.add_host(my_host)
|
||||||
|
|
||||||
|
self.inventory.add_group(my_group)
|
||||||
|
|
||||||
|
def gen_inventory(self):
|
||||||
|
"""
|
||||||
|
add hosts to inventory.
|
||||||
|
"""
|
||||||
|
if isinstance(self.resource, list):
|
||||||
|
self.my_add_group(self.resource, 'default_group')
|
||||||
|
elif isinstance(self.resource, dict):
|
||||||
|
for groupname, hosts_and_vars in self.resource.iteritems():
|
||||||
|
self.my_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_raw = {}
|
||||||
|
|
||||||
|
def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='*',
|
||||||
|
become=False, become_method='sudo', become_user='root', become_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=become,
|
||||||
|
become_method=become_method,
|
||||||
|
become_user=become_user,
|
||||||
|
become_pass=become_pass
|
||||||
|
)
|
||||||
|
self.results_raw = hoc.run()
|
||||||
|
logger.debug(self.results_raw)
|
||||||
|
return self.results_raw
|
||||||
|
|
||||||
|
@property
|
||||||
|
def results(self):
|
||||||
|
"""
|
||||||
|
{'failed': {'localhost': ''}, 'ok': {'jumpserver': ''}}
|
||||||
|
"""
|
||||||
|
result = {'failed': {}, 'ok': {}}
|
||||||
|
dark = self.results_raw.get('dark')
|
||||||
|
contacted = self.results_raw.get('contacted')
|
||||||
|
if dark:
|
||||||
|
for host, info in dark.items():
|
||||||
|
result['failed'][host] = info.get('msg')
|
||||||
|
|
||||||
|
if contacted:
|
||||||
|
for host, info in contacted.items():
|
||||||
|
if info.get('invocation').get('module_name') in ['raw', 'shell', 'command', 'script']:
|
||||||
|
if info.get('rc') == 0:
|
||||||
|
result['ok'][host] = info.get('stdout') + info.get('stderr')
|
||||||
|
else:
|
||||||
|
result['failed'][host] = info.get('stdout') + info.get('stderr')
|
||||||
|
else:
|
||||||
|
if info.get('failed'):
|
||||||
|
result['failed'][host] = info.get('msg')
|
||||||
|
else:
|
||||||
|
result['ok'][host] = info.get('changed')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
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_raw = {}
|
||||||
|
|
||||||
|
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'")
|
||||||
|
hoc = Runner(module_name=module_name,
|
||||||
|
module_args=command,
|
||||||
|
timeout=timeout,
|
||||||
|
inventory=self.inventory,
|
||||||
|
pattern=pattern,
|
||||||
|
forks=forks,
|
||||||
|
)
|
||||||
|
self.results_raw = hoc.run()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def result(self):
|
||||||
|
result = {}
|
||||||
|
for k, v in self.results_raw.items():
|
||||||
|
if k == 'dark':
|
||||||
|
for host, info in v.items():
|
||||||
|
result[host] = {'dark': info.get('msg')}
|
||||||
|
elif k == 'contacted':
|
||||||
|
for host, info in v.items():
|
||||||
|
result[host] = {}
|
||||||
|
if info.get('stdout'):
|
||||||
|
result[host]['stdout'] = info.get('stdout')
|
||||||
|
elif info.get('stderr'):
|
||||||
|
result[host]['stderr'] = info.get('stderr')
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
result = {}
|
||||||
|
if self.stdout:
|
||||||
|
result['ok'] = self.stdout
|
||||||
|
if self.stderr:
|
||||||
|
result['err'] = self.stderr
|
||||||
|
if self.dark:
|
||||||
|
result['dark'] = self.dark
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def exec_time(self):
|
||||||
|
"""
|
||||||
|
get the command execute time.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
all = self.results_raw.get("contacted")
|
||||||
|
for key, value in all.iteritems():
|
||||||
|
result[key] = {
|
||||||
|
"start": value.get("start"),
|
||||||
|
"end" : value.get("end"),
|
||||||
|
"delta": value.get("delta"),}
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stdout(self):
|
||||||
|
"""
|
||||||
|
get the comamnd standard output.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
all = self.results_raw.get("contacted")
|
||||||
|
for key, value in all.iteritems():
|
||||||
|
result[key] = value.get("stdout")
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stderr(self):
|
||||||
|
"""
|
||||||
|
get the command standard error.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
all = self.results_raw.get("contacted")
|
||||||
|
for key, value in all.iteritems():
|
||||||
|
if value.get("stderr") or value.get("warnings"):
|
||||||
|
result[key] = {
|
||||||
|
"stderr": value.get("stderr"),
|
||||||
|
"warnings": value.get("warnings"),}
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dark(self):
|
||||||
|
"""
|
||||||
|
get the dark results.
|
||||||
|
"""
|
||||||
|
return self.results_raw.get("dark")
|
||||||
|
|
||||||
|
|
||||||
|
class MyTask(MyRunner):
|
||||||
|
"""
|
||||||
|
this is a tasks object for include the common command.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(MyTask, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def push_key(self, user, key_path):
|
||||||
|
"""
|
||||||
|
push the ssh authorized key to target.
|
||||||
|
"""
|
||||||
|
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state=present' % (user, key_path)
|
||||||
|
self.run("authorized_key", module_args, become=True)
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def push_multi_key(self, **user_info):
|
||||||
|
"""
|
||||||
|
push multi key
|
||||||
|
:param user_info:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
ret_failed = []
|
||||||
|
ret_success = []
|
||||||
|
for user, key_path in user_info.iteritems():
|
||||||
|
ret = self.push_key(user, key_path)
|
||||||
|
if ret.get("status") == "ok":
|
||||||
|
ret_success.append(ret)
|
||||||
|
if ret.get("status") == "failed":
|
||||||
|
ret_failed.append(ret)
|
||||||
|
|
||||||
|
if ret_failed:
|
||||||
|
return {"status": "failed", "msg": ret_failed}
|
||||||
|
else:
|
||||||
|
return {"status": "success", "msg": ret_success}
|
||||||
|
|
||||||
|
def del_key(self, user, key_path):
|
||||||
|
"""
|
||||||
|
push the ssh authorized key to target.
|
||||||
|
"""
|
||||||
|
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
|
||||||
|
self.run("authorized_key", module_args, become=True)
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def add_user(self, username, password=''):
|
||||||
|
"""
|
||||||
|
add a host user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if password:
|
||||||
|
encrypt_pass = sha512_crypt.encrypt(password)
|
||||||
|
module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass)
|
||||||
|
else:
|
||||||
|
module_args = 'name=%s shell=/bin/bash' % username
|
||||||
|
|
||||||
|
self.run("user", module_args, become=True)
|
||||||
|
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def add_multi_user(self, **user_info):
|
||||||
|
"""
|
||||||
|
add multi user
|
||||||
|
:param user_info: keyword args
|
||||||
|
{username: password}
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
ret_success = []
|
||||||
|
ret_failed = []
|
||||||
|
for user, password in user_info.iteritems():
|
||||||
|
ret = self.add_user(user, password)
|
||||||
|
if ret.get("status") == "ok":
|
||||||
|
ret_success.append(ret)
|
||||||
|
if ret.get("status") == "failed":
|
||||||
|
ret_failed.append(ret)
|
||||||
|
|
||||||
|
if ret_failed:
|
||||||
|
return {"status": "failed", "msg": ret_failed}
|
||||||
|
else:
|
||||||
|
return {"status": "success", "msg": ret_success}
|
||||||
|
|
||||||
|
def del_user(self, username):
|
||||||
|
"""
|
||||||
|
delete a host user.
|
||||||
|
"""
|
||||||
|
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % username
|
||||||
|
self.run("user", module_args, become=True)
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gen_sudo_script(role_list, sudo_list):
|
||||||
|
# receive role_list = [role1, role2] sudo_list = [sudo1, sudo2]
|
||||||
|
# return sudo_alias={'NETWORK': '/sbin/ifconfig, /ls'} sudo_user={'user1': ['NETWORK', 'SYSTEM']}
|
||||||
|
sudo_alias = {}
|
||||||
|
sudo_user = {}
|
||||||
|
for sudo in sudo_list:
|
||||||
|
sudo_alias[sudo.name] = sudo.commands
|
||||||
|
|
||||||
|
for role in role_list:
|
||||||
|
sudo_user[role.name] = ','.join(sudo_alias.keys())
|
||||||
|
|
||||||
|
sudo_j2 = get_template('jperm/role_sudo.j2')
|
||||||
|
sudo_content = sudo_j2.render(Context({"sudo_alias": sudo_alias, "sudo_user": sudo_user}))
|
||||||
|
sudo_file = NamedTemporaryFile(delete=False)
|
||||||
|
sudo_file.write(sudo_content)
|
||||||
|
sudo_file.close()
|
||||||
|
return sudo_file.name
|
||||||
|
|
||||||
|
def push_sudo_file(self, role_list, sudo_list):
|
||||||
|
"""
|
||||||
|
use template to render pushed sudoers file
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
module_args1 = self.gen_sudo_script(role_list, sudo_list)
|
||||||
|
self.run("script", module_args1, become=True)
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
|
||||||
|
class CustomAggregateStats(callbacks.AggregateStats):
|
||||||
|
"""
|
||||||
|
Holds stats about per-host activity during playbook runs.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(CustomAggregateStats, self).__init__()
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
def compute(self, runner_results, setup=False, poll=False,
|
||||||
|
ignore_errors=False):
|
||||||
|
"""
|
||||||
|
Walk through all results and increment stats.
|
||||||
|
"""
|
||||||
|
super(CustomAggregateStats, self).compute(runner_results, setup, poll,
|
||||||
|
ignore_errors)
|
||||||
|
|
||||||
|
self.results.append(runner_results)
|
||||||
|
|
||||||
|
def summarize(self, host):
|
||||||
|
"""
|
||||||
|
Return information about a particular host
|
||||||
|
"""
|
||||||
|
summarized_info = super(CustomAggregateStats, self).summarize(host)
|
||||||
|
|
||||||
|
# Adding the info I need
|
||||||
|
summarized_info['result'] = self.results
|
||||||
|
|
||||||
|
return summarized_info
|
||||||
|
|
||||||
|
|
||||||
|
class MyPlaybook(MyInventory):
|
||||||
|
"""
|
||||||
|
this is my playbook object for execute playbook.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(MyPlaybook, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def run(self, playbook_relational_path, extra_vars=None):
|
||||||
|
"""
|
||||||
|
run ansible playbook,
|
||||||
|
only surport relational path.
|
||||||
|
"""
|
||||||
|
stats = callbacks.AggregateStats()
|
||||||
|
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
|
||||||
|
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
|
||||||
|
playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path)
|
||||||
|
|
||||||
|
pb = PlayBook(
|
||||||
|
playbook=playbook_path,
|
||||||
|
stats=stats,
|
||||||
|
callbacks=playbook_cb,
|
||||||
|
runner_callbacks=runner_cb,
|
||||||
|
inventory=self.inventory,
|
||||||
|
extra_vars=extra_vars,
|
||||||
|
check=False)
|
||||||
|
|
||||||
|
self.results = pb.run()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_results(self):
|
||||||
|
"""
|
||||||
|
get the raw results after playbook run.
|
||||||
|
"""
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
|
||||||
|
class App(MyPlaybook):
|
||||||
|
"""
|
||||||
|
this is a app object for inclue the common playbook.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(App, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# resource = {
|
||||||
|
# "group1": {
|
||||||
|
# "hosts": [{"hostname": "127.0.0.1", "port": "22", "username": "root", "password": "xxx"},],
|
||||||
|
# "vars" : {"var1": "value1", "var2": "value2"},
|
||||||
|
# },
|
||||||
|
# }
|
||||||
|
|
||||||
|
resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902",
|
||||||
|
# "ansible_become": "yes",
|
||||||
|
# "ansible_become_method": "sudo",
|
||||||
|
# # "ansible_become_user": "root",
|
||||||
|
# "ansible_become_pass": "yusky0902",
|
||||||
|
}]
|
||||||
|
cmd = Command(resource)
|
||||||
|
print cmd.run('ls')
|
||||||
|
|
||||||
|
# resource = [{"hostname": "192.168.10.148", "port": "22", "username": "root", "password": "xxx"}]
|
||||||
|
# task = Tasks(resource)
|
||||||
|
# print task.get_host_info()
|
||||||
|
|
||||||
|
# playbook = MyPlaybook(resource)
|
||||||
|
# playbook.run('test.yml')
|
||||||
|
# print playbook.raw_results
|
||||||
|
|
||||||
|
# task = Tasks(resource)
|
||||||
|
# print task.add_user('test', 'mypass')
|
||||||
|
# print task.del_user('test')
|
||||||
|
# print task.push_key('root', '/root/.ssh/id_rsa.pub')
|
||||||
|
# print task.del_key('root', '/root/.ssh/id_rsa.pub')
|
||||||
|
|
||||||
|
# task = Tasks(resource)
|
||||||
|
# print task.add_init_users()
|
||||||
|
# print task.del_init_users()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
from jumpserver.api import *
|
||||||
|
import uuid
|
||||||
|
import re
|
||||||
|
|
||||||
|
from jumpserver.models import Setting
|
||||||
|
from jperm.models import PermRole, PermPush, 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,]
|
||||||
|
'role': {role1: {'asset': []}, 'asset_group': []}, role2: {}},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
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'] = {}
|
||||||
|
perm_role = perm['role'] = {}
|
||||||
|
for rule in rule_all:
|
||||||
|
asset_groups = rule.asset_group.all()
|
||||||
|
assets = rule.asset.all()
|
||||||
|
perm_roles = rule.role.all()
|
||||||
|
group_assets = []
|
||||||
|
for asset_group in asset_groups:
|
||||||
|
group_assets.extend(asset_group.asset_set.all())
|
||||||
|
# 获取一个规则授权的角色和对应主机
|
||||||
|
for role in perm_roles:
|
||||||
|
if perm_role.get(role):
|
||||||
|
perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets).union(set(group_assets)))
|
||||||
|
perm_role[role]['asset_group'] = perm_role[role].get('asset_group', set()).union(set(asset_groups))
|
||||||
|
else:
|
||||||
|
perm_role[role] = {'asset': set(assets).union(set(group_assets)), 'asset_group': set(asset_groups)}
|
||||||
|
|
||||||
|
# 获取一个规则用户授权的资产
|
||||||
|
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 user_have_perm(user, asset):
|
||||||
|
user_perm_all = get_group_user_perm(user)
|
||||||
|
user_assets = user_perm_all.get('asset').keys()
|
||||||
|
if asset in user_assets:
|
||||||
|
return user_perm_all.get('asset').get(asset).get('role')
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def gen_resource(ob, perm=None):
|
||||||
|
"""
|
||||||
|
ob为用户或资产列表或资产queryset, 如果同时输入用户和{'role': role1, 'asset': []},则获取用户在这些资产上的信息
|
||||||
|
生成MyInventory需要的 resource文件
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
if isinstance(ob, dict):
|
||||||
|
role = ob.get('role')
|
||||||
|
asset_r = ob.get('asset')
|
||||||
|
user = ob.get('user')
|
||||||
|
if not perm:
|
||||||
|
perm = get_group_user_perm(user)
|
||||||
|
|
||||||
|
if role:
|
||||||
|
roles = perm.get('role', {}).keys() # 获取用户所有授权角色
|
||||||
|
if role not in roles:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
role_assets_all = perm.get('role').get(role).get('asset') # 获取用户该角色所有授权主机
|
||||||
|
assets = set(role_assets_all) & set(asset_r) # 获取用户提交中合法的主机
|
||||||
|
|
||||||
|
for asset in assets:
|
||||||
|
asset_info = get_asset_info(asset)
|
||||||
|
role_key = get_role_key(user, role)
|
||||||
|
info = {'hostname': asset.hostname,
|
||||||
|
'ip': asset.ip,
|
||||||
|
'port': asset_info.get('port', 22),
|
||||||
|
'username': role.name,
|
||||||
|
'password': CRYPTOR.decrypt(role.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.path.isfile(role_key):
|
||||||
|
info['ssh_key'] = role_key
|
||||||
|
|
||||||
|
res.append(info)
|
||||||
|
else:
|
||||||
|
for asset, asset_info in perm.get('asset').items():
|
||||||
|
if asset not in asset_r:
|
||||||
|
continue
|
||||||
|
asset_info = get_asset_info(asset)
|
||||||
|
try:
|
||||||
|
role = sorted(list(perm.get('asset').get(asset).get('role')))[0]
|
||||||
|
except IndexError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
role_key = get_role_key(user, role)
|
||||||
|
info = {'hostname': asset.hostname,
|
||||||
|
'ip': asset.ip,
|
||||||
|
'port': asset_info.get('port', 22),
|
||||||
|
'username': role.name,
|
||||||
|
'password': CRYPTOR.decrypt(role.password),
|
||||||
|
}
|
||||||
|
if os.path.isfile(role_key):
|
||||||
|
info['ssh_key'] = role_key
|
||||||
|
|
||||||
|
res.append(info)
|
||||||
|
|
||||||
|
elif 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'] = CRYPTOR.decrypt(role.password)
|
||||||
|
|
||||||
|
role_key = get_role_key(ob, role)
|
||||||
|
if os.path.isfile(role_key):
|
||||||
|
info['ssh_key'] = role_key
|
||||||
|
res.append(info)
|
||||||
|
|
||||||
|
elif isinstance(ob, (list, QuerySet)):
|
||||||
|
for asset in ob:
|
||||||
|
info = get_asset_info(asset)
|
||||||
|
res.append(info)
|
||||||
|
logger.debug('生成res: %s' % res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def get_object_list(model, id_list):
|
||||||
|
"""根据id列表获取对象列表"""
|
||||||
|
object_list = []
|
||||||
|
for object_id in id_list:
|
||||||
|
if object_id:
|
||||||
|
object_list.extend(model.objects.filter(id=int(object_id)))
|
||||||
|
|
||||||
|
return object_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_role_info(role_id, type="all"):
|
||||||
|
"""
|
||||||
|
获取role对应的一些信息
|
||||||
|
:return: 返回值 均为对象列表
|
||||||
|
"""
|
||||||
|
# 获取role对应的授权规则
|
||||||
|
role_obj = PermRole.objects.get(id=role_id)
|
||||||
|
rule_push_obj = role_obj.perm_rule.all()
|
||||||
|
# 获取role 对应的用户 和 用户组
|
||||||
|
# 获取role 对应的主机 和主机组
|
||||||
|
users_obj = []
|
||||||
|
assets_obj = []
|
||||||
|
user_groups_obj = []
|
||||||
|
asset_groups_obj = []
|
||||||
|
for push in rule_push_obj:
|
||||||
|
for user in push.user.all():
|
||||||
|
users_obj.append(user)
|
||||||
|
for asset in push.asset.all():
|
||||||
|
assets_obj.append(asset)
|
||||||
|
for user_group in push.user_group.all():
|
||||||
|
user_groups_obj.append(user_group)
|
||||||
|
for asset_group in push.asset_group.all():
|
||||||
|
asset_groups_obj.append(asset_group)
|
||||||
|
|
||||||
|
if type == "all":
|
||||||
|
return {"rules": set(rule_push_obj),
|
||||||
|
"users": set(users_obj),
|
||||||
|
"user_groups": set(user_groups_obj),
|
||||||
|
"assets": set(assets_obj),
|
||||||
|
"asset_groups": set(asset_groups_obj),
|
||||||
|
}
|
||||||
|
|
||||||
|
elif type == "rule":
|
||||||
|
return set(rule_push_obj)
|
||||||
|
elif type == "user":
|
||||||
|
return set(users_obj)
|
||||||
|
elif type == "user_group":
|
||||||
|
return set(user_groups_obj)
|
||||||
|
elif type == "asset":
|
||||||
|
return set(assets_obj)
|
||||||
|
elif type == "asset_group":
|
||||||
|
return set(asset_groups_obj)
|
||||||
|
else:
|
||||||
|
return u"不支持的查询"
|
||||||
|
|
||||||
|
|
||||||
|
def get_role_push_host(role):
|
||||||
|
"""
|
||||||
|
asset_pushed: {'success': push.success, 'key': push.is_public_key, 'password': push.is_password,
|
||||||
|
'result': push.result}
|
||||||
|
asset_no_push: set(asset1, asset2)
|
||||||
|
"""
|
||||||
|
# 计算该role 所有push记录 总共推送的主机
|
||||||
|
pushs = PermPush.objects.filter(role=role)
|
||||||
|
asset_all = Asset.objects.all()
|
||||||
|
asset_pushed = {}
|
||||||
|
for push in pushs:
|
||||||
|
asset_pushed[push.asset] = {'success': push.success, 'key': push.is_public_key, 'password': push.is_password,
|
||||||
|
'result': push.result}
|
||||||
|
asset_no_push = set(asset_all) - set(asset_pushed.keys())
|
||||||
|
return asset_pushed, asset_no_push
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print get_role_info(1)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- hosts: 'add_users_group'
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- name: add SA user
|
||||||
|
command: uname -a
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- hosts: test
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- name: just for test
|
||||||
|
command: uname -a
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import random
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
from paramiko import SSHException
|
||||||
|
from paramiko.rsakey import RSAKey
|
||||||
|
from jumpserver.api import mkdir
|
||||||
|
from uuid import uuid4
|
||||||
|
from jumpserver.api import CRYPTOR
|
||||||
|
from os import makedirs
|
||||||
|
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
from jumpserver.settings import KEY_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def get_rand_pass():
|
||||||
|
"""
|
||||||
|
get a reandom password.
|
||||||
|
"""
|
||||||
|
CRYPTOR.gen_rand_pass(20)
|
||||||
|
|
||||||
|
|
||||||
|
def updates_dict(*args):
|
||||||
|
"""
|
||||||
|
surport update multi dict
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for d in args:
|
||||||
|
result.update(d)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def gen_keys(key="", key_path_dir=""):
|
||||||
|
"""
|
||||||
|
在KEY_DIR下创建一个 uuid命名的目录,
|
||||||
|
并且在该目录下 生产一对秘钥
|
||||||
|
:return: 返回目录名(uuid)
|
||||||
|
"""
|
||||||
|
key_basename = "key-" + uuid4().hex
|
||||||
|
if not key_path_dir:
|
||||||
|
key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename)
|
||||||
|
private_key = os.path.join(key_path_dir, 'id_rsa')
|
||||||
|
public_key = os.path.join(key_path_dir, 'id_rsa.pub')
|
||||||
|
mkdir(key_path_dir, mode=0755)
|
||||||
|
if not key:
|
||||||
|
key = RSAKey.generate(2048)
|
||||||
|
key.write_private_key_file(private_key)
|
||||||
|
else:
|
||||||
|
key_file = os.path.join(key_path_dir, 'id_rsa')
|
||||||
|
with open(key_file, 'w') as f:
|
||||||
|
f.write(key)
|
||||||
|
f.close()
|
||||||
|
with open(key_file) as f:
|
||||||
|
try:
|
||||||
|
key = RSAKey.from_private_key(f)
|
||||||
|
except SSHException:
|
||||||
|
shutil.rmtree(key_path_dir, ignore_errors=True)
|
||||||
|
raise SSHException
|
||||||
|
os.chmod(private_key, 0644)
|
||||||
|
|
||||||
|
with open(public_key, 'w') as content_file:
|
||||||
|
for data in [key.get_name(),
|
||||||
|
" ",
|
||||||
|
key.get_base64(),
|
||||||
|
" %s@%s" % ("jumpserver", os.uname()[1])]:
|
||||||
|
content_file.write(data)
|
||||||
|
return key_path_dir
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print gen_keys()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Setting(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
field1 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
field2 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
field3 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
field4 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
field5 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = u'setting'
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
|
@ -0,0 +1,48 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from ansible.playbook import PlayBook
|
||||||
|
from ansible import callbacks, utils
|
||||||
|
|
||||||
|
|
||||||
|
def playbook_run(inventory, playbook, default_user=None, default_port=None, default_pri_key_path=None):
|
||||||
|
stats = callbacks.AggregateStats()
|
||||||
|
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
|
||||||
|
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
|
||||||
|
# run the playbook
|
||||||
|
print default_user, default_port, default_pri_key_path, inventory, playbook
|
||||||
|
if default_user and default_port and default_pri_key_path:
|
||||||
|
playbook = PlayBook(host_list=inventory,
|
||||||
|
playbook=playbook,
|
||||||
|
forks=5,
|
||||||
|
remote_user=default_user,
|
||||||
|
remote_port=default_port,
|
||||||
|
private_key_file=default_pri_key_path,
|
||||||
|
callbacks=playbook_cb,
|
||||||
|
runner_callbacks=runner_cb,
|
||||||
|
stats=stats,
|
||||||
|
become=True,
|
||||||
|
become_user='root')
|
||||||
|
else:
|
||||||
|
playbook = PlayBook(host_list=inventory,
|
||||||
|
playbook=playbook,
|
||||||
|
forks=5,
|
||||||
|
callbacks=playbook_cb,
|
||||||
|
runner_callbacks=runner_cb,
|
||||||
|
stats=stats,
|
||||||
|
become=True,
|
||||||
|
become_user='root')
|
||||||
|
results = playbook.run()
|
||||||
|
print results
|
||||||
|
results_r = {'unreachable': [], 'failures': [], 'success': []}
|
||||||
|
for hostname, result in results.items():
|
||||||
|
if result.get('unreachable', 2):
|
||||||
|
results_r['unreachable'].append(hostname)
|
||||||
|
print "%s >>> unreachable" % hostname
|
||||||
|
elif result.get('failures', 2):
|
||||||
|
results_r['failures'].append(hostname)
|
||||||
|
print "%s >>> Failed" % hostname
|
||||||
|
else:
|
||||||
|
results_r['success'].append(hostname)
|
||||||
|
print "%s >>> Success" % hostname
|
||||||
|
return results_r
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from subprocess import call
|
||||||
|
|
||||||
|
from juser.models import AdminGroup
|
||||||
|
from jumpserver.api import *
|
||||||
|
from jumpserver.settings import BASE_DIR, EMAIL_HOST_USER as MAIL_FROM
|
||||||
|
|
||||||
|
|
||||||
|
def group_add_user(group, user_id=None, username=None):
|
||||||
|
"""
|
||||||
|
用户组中添加用户
|
||||||
|
UserGroup Add a user
|
||||||
|
"""
|
||||||
|
if user_id:
|
||||||
|
user = get_object(User, id=user_id)
|
||||||
|
else:
|
||||||
|
user = get_object(User, username=username)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
group.user_set.add(user)
|
||||||
|
|
||||||
|
|
||||||
|
def db_add_group(**kwargs):
|
||||||
|
"""
|
||||||
|
add a user group in database
|
||||||
|
数据库中添加用户组
|
||||||
|
"""
|
||||||
|
name = kwargs.get('name')
|
||||||
|
group = get_object(UserGroup, name=name)
|
||||||
|
users = kwargs.pop('users_id')
|
||||||
|
|
||||||
|
if not group:
|
||||||
|
group = UserGroup(**kwargs)
|
||||||
|
group.save()
|
||||||
|
for user_id in users:
|
||||||
|
group_add_user(group, user_id)
|
||||||
|
|
||||||
|
|
||||||
|
def group_update_member(group_id, users_id_list):
|
||||||
|
"""
|
||||||
|
user group update member
|
||||||
|
用户组更新成员
|
||||||
|
"""
|
||||||
|
group = get_object(UserGroup, id=group_id)
|
||||||
|
if group:
|
||||||
|
group.user_set.clear()
|
||||||
|
for user_id in users_id_list:
|
||||||
|
user = get_object(UserGroup, id=user_id)
|
||||||
|
if isinstance(user, UserGroup):
|
||||||
|
group.user_set.add(user)
|
||||||
|
|
||||||
|
|
||||||
|
def db_add_user(**kwargs):
|
||||||
|
"""
|
||||||
|
add a user in database
|
||||||
|
数据库中添加用户
|
||||||
|
"""
|
||||||
|
groups_post = kwargs.pop('groups')
|
||||||
|
admin_groups = kwargs.pop('admin_groups')
|
||||||
|
role = kwargs.get('role', 'CU')
|
||||||
|
user = User(**kwargs)
|
||||||
|
user.set_password(kwargs.get('password'))
|
||||||
|
user.save()
|
||||||
|
if groups_post:
|
||||||
|
group_select = []
|
||||||
|
for group_id in groups_post:
|
||||||
|
group = UserGroup.objects.filter(id=group_id)
|
||||||
|
group_select.extend(group)
|
||||||
|
user.group = group_select
|
||||||
|
|
||||||
|
if admin_groups and role == 'GA': # 如果是组管理员就要添加组管理员和组到管理组中
|
||||||
|
for group_id in admin_groups:
|
||||||
|
group = get_object(UserGroup, id=group_id)
|
||||||
|
if group:
|
||||||
|
AdminGroup(user=user, group=group).save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def db_update_user(**kwargs):
|
||||||
|
"""
|
||||||
|
update a user info in database
|
||||||
|
数据库更新用户信息
|
||||||
|
"""
|
||||||
|
groups_post = kwargs.pop('groups')
|
||||||
|
admin_groups_post = kwargs.pop('admin_groups')
|
||||||
|
user_id = kwargs.pop('user_id')
|
||||||
|
user = User.objects.filter(id=user_id)
|
||||||
|
user_get = User.objects.get(id=user_id)
|
||||||
|
if user:
|
||||||
|
pwd = kwargs.pop('password')
|
||||||
|
user.update(**kwargs)
|
||||||
|
if pwd != '':
|
||||||
|
user_get.set_password(pwd)
|
||||||
|
user_get.save()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
group_select = []
|
||||||
|
if groups_post:
|
||||||
|
for group_id in groups_post:
|
||||||
|
group = UserGroup.objects.filter(id=group_id)
|
||||||
|
group_select.extend(group)
|
||||||
|
user_get.group = group_select
|
||||||
|
|
||||||
|
if admin_groups_post != '':
|
||||||
|
user_get.admingroup_set.all().delete()
|
||||||
|
for group_id in admin_groups_post:
|
||||||
|
group = get_object(UserGroup, id=group_id)
|
||||||
|
AdminGroup(user=user, group=group).save()
|
||||||
|
|
||||||
|
|
||||||
|
def db_del_user(username):
|
||||||
|
"""
|
||||||
|
delete a user from database
|
||||||
|
从数据库中删除用户
|
||||||
|
"""
|
||||||
|
user = get_object(User, username=username)
|
||||||
|
if user:
|
||||||
|
user.delete()
|
||||||
|
|
||||||
|
|
||||||
|
def gen_ssh_key(username, password='',
|
||||||
|
key_dir=os.path.join(KEY_DIR, 'user'),
|
||||||
|
authorized_keys=True, home="/home", length=2048):
|
||||||
|
"""
|
||||||
|
generate a user ssh key in a property dir
|
||||||
|
生成一个用户ssh密钥对
|
||||||
|
"""
|
||||||
|
logger.debug('生成ssh key, 并设置authorized_keys')
|
||||||
|
private_key_file = os.path.join(key_dir, username+'.pem')
|
||||||
|
mkdir(key_dir, mode=0777)
|
||||||
|
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))
|
||||||
|
|
||||||
|
if authorized_keys:
|
||||||
|
auth_key_dir = os.path.join(home, username, '.ssh')
|
||||||
|
mkdir(auth_key_dir, username=username , mode=0700)
|
||||||
|
authorized_key_file = os.path.join(auth_key_dir, 'authorized_keys')
|
||||||
|
with open(private_key_file+'.pub') as pub_f:
|
||||||
|
with open(authorized_key_file, 'w') as auth_f:
|
||||||
|
auth_f.write(pub_f.read())
|
||||||
|
os.chmod(authorized_key_file, 0600)
|
||||||
|
chown(authorized_key_file, username)
|
||||||
|
|
||||||
|
|
||||||
|
def server_add_user(username, password, ssh_key_pwd='', ssh_key_login_need=True):
|
||||||
|
"""
|
||||||
|
add a system user in jumpserver
|
||||||
|
在jumpserver服务器上添加一个用户
|
||||||
|
"""
|
||||||
|
bash("useradd '%s'; echo '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, password, username))
|
||||||
|
if ssh_key_login_need:
|
||||||
|
gen_ssh_key(username, ssh_key_pwd)
|
||||||
|
|
||||||
|
|
||||||
|
def user_add_mail(user, kwargs):
|
||||||
|
"""
|
||||||
|
add user send mail
|
||||||
|
发送用户添加邮件
|
||||||
|
"""
|
||||||
|
user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'}
|
||||||
|
mail_title = u'恭喜你的跳板机用户 %s 添加成功 Jumpserver' % user.name
|
||||||
|
mail_msg = u"""
|
||||||
|
Hi, %s
|
||||||
|
您的用户名: %s
|
||||||
|
您的权限: %s
|
||||||
|
您的web登录密码: %s
|
||||||
|
您的ssh密钥文件密码: %s
|
||||||
|
密钥下载地址: %s/juser/key/down/?uuid=%s
|
||||||
|
说明: 请登陆后再下载密钥!
|
||||||
|
""" % (user.name, user.username, user_role.get(user.role, u'普通用户'),
|
||||||
|
kwargs.get('password'), kwargs.get('ssh_key_pwd'), URL, user.uuid)
|
||||||
|
send_mail(mail_title, mail_msg, MAIL_FROM, [user.email], fail_silently=False)
|
||||||
|
|
||||||
|
|
||||||
|
def server_del_user(username):
|
||||||
|
"""
|
||||||
|
delete a user from jumpserver linux system
|
||||||
|
删除系统上的某用户
|
||||||
|
"""
|
||||||
|
bash('userdel -r %s' % username)
|
||||||
|
|
||||||
|
|
||||||
|
def get_display_msg(user, password, ssh_key_pwd, ssh_key_login_need, send_mail_need):
|
||||||
|
if send_mail_need:
|
||||||
|
msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (user.name, user.email)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
if ssh_key_login_need:
|
||||||
|
msg = u"""
|
||||||
|
跳板机地址: %s
|
||||||
|
用户名:%s
|
||||||
|
密码:%s
|
||||||
|
密钥密码:%s
|
||||||
|
密钥下载url: %s/juser/key/down/?uuid=%s
|
||||||
|
该账号密码可以登陆web和跳板机。
|
||||||
|
""" % (URL, user.username, password, ssh_key_pwd, URL, user.uuid)
|
||||||
|
else:
|
||||||
|
msg = u"""
|
||||||
|
跳板机地址: %s \n
|
||||||
|
用户名:%s \n
|
||||||
|
密码:%s \n
|
||||||
|
该账号密码可以登陆web和跳板机。
|
||||||
|
""" % (URL, user.username, password)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
import threading
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.options
|
||||||
|
import tornado.web
|
||||||
|
import tornado.websocket
|
||||||
|
import tornado.httpserver
|
||||||
|
import tornado.gen
|
||||||
|
import tornado.httpclient
|
||||||
|
from tornado.websocket import WebSocketClosedError
|
||||||
|
|
||||||
|
from tornado.options import define, options
|
||||||
|
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier
|
||||||
|
import select
|
||||||
|
|
||||||
|
from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource
|
||||||
|
from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as json
|
||||||
|
except ImportError:
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
define("port", default=3000, help="run on the given port", type=int)
|
||||||
|
define("host", default='0.0.0.0', help="run port on", type=str)
|
||||||
|
|
||||||
|
|
||||||
|
def require_auth(role='user'):
|
||||||
|
def _deco(func):
|
||||||
|
def _deco2(request, *args, **kwargs):
|
||||||
|
if request.get_cookie('sessionid'):
|
||||||
|
session_key = request.get_cookie('sessionid')
|
||||||
|
else:
|
||||||
|
session_key = request.get_argument('sessionid', '')
|
||||||
|
|
||||||
|
logger.debug('Websocket: session_key: %s' % session_key)
|
||||||
|
if session_key:
|
||||||
|
session = get_object(Session, session_key=session_key)
|
||||||
|
logger.debug('Websocket: session: %s' % session)
|
||||||
|
if session and datetime.datetime.now() < session.expire_date:
|
||||||
|
user_id = session.get_decoded().get('_auth_user_id')
|
||||||
|
user = get_object(User, id=user_id)
|
||||||
|
if user:
|
||||||
|
logger.debug('Websocket: user [ %s ] request websocket' % user.username)
|
||||||
|
request.user = user
|
||||||
|
if role == 'admin':
|
||||||
|
if user.role in ['SU', 'GA']:
|
||||||
|
return func(request, *args, **kwargs)
|
||||||
|
logger.debug('Websocket: user [ %s ] is not admin.' % user.username)
|
||||||
|
else:
|
||||||
|
return func(request, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.debug('Websocket: session expired: %s' % session_key)
|
||||||
|
try:
|
||||||
|
request.close()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
logger.warning('Websocket: Request auth failed.')
|
||||||
|
return _deco2
|
||||||
|
return _deco
|
||||||
|
|
||||||
|
|
||||||
|
class MyThread(threading.Thread):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(MyThread, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
super(MyThread, self).run()
|
||||||
|
except WebSocketClosedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandler(ProcessEvent):
|
||||||
|
def __init__(self, client=None):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def process_IN_MODIFY(self, event):
|
||||||
|
self.client.write_message(f.read())
|
||||||
|
|
||||||
|
|
||||||
|
def file_monitor(path='.', client=None):
|
||||||
|
wm = WatchManager()
|
||||||
|
mask = IN_DELETE | IN_CREATE | IN_MODIFY
|
||||||
|
notifier = AsyncNotifier(wm, EventHandler(client))
|
||||||
|
wm.add_watch(path, mask, auto_add=True, rec=True)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
logger.debug("File %s does not exist." % path)
|
||||||
|
sys.exit(3)
|
||||||
|
else:
|
||||||
|
logger.debug("Now starting monitor file %s." % path)
|
||||||
|
global f
|
||||||
|
f = open(path, 'r')
|
||||||
|
st_size = os.stat(path)[6]
|
||||||
|
f.seek(st_size)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
notifier.process_events()
|
||||||
|
if notifier.check_events():
|
||||||
|
notifier.read_events()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print "keyboard Interrupt."
|
||||||
|
notifier.stop()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
class MonitorHandler(tornado.websocket.WebSocketHandler):
|
||||||
|
clients = []
|
||||||
|
threads = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.file_path = None
|
||||||
|
super(self.__class__, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def check_origin(self, origin):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@require_auth('admin')
|
||||||
|
def open(self):
|
||||||
|
# 获取监控的path
|
||||||
|
self.file_path = self.get_argument('file_path', '')
|
||||||
|
MonitorHandler.clients.append(self)
|
||||||
|
thread = MyThread(target=file_monitor, args=('%s.log' % self.file_path, self))
|
||||||
|
MonitorHandler.threads.append(thread)
|
||||||
|
self.stream.set_nodelay(True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for t in MonitorHandler.threads:
|
||||||
|
if t.is_alive():
|
||||||
|
continue
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
except WebSocketClosedError:
|
||||||
|
client_index = MonitorHandler.clients.index(self)
|
||||||
|
MonitorHandler.threads[client_index].stop()
|
||||||
|
MonitorHandler.clients.remove(self)
|
||||||
|
MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
|
||||||
|
|
||||||
|
logger.debug("Websocket: Monitor client num: %s, thread num: %s" % (len(MonitorHandler.clients),
|
||||||
|
len(MonitorHandler.threads)))
|
||||||
|
|
||||||
|
def on_message(self, message):
|
||||||
|
# 监控日志,发生变动发向客户端
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
# 客户端主动关闭
|
||||||
|
# self.close()
|
||||||
|
|
||||||
|
logger.debug("Websocket: Monitor client close request")
|
||||||
|
try:
|
||||||
|
client_index = MonitorHandler.clients.index(self)
|
||||||
|
MonitorHandler.clients.remove(self)
|
||||||
|
MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WebTty(Tty):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(WebTty, self).__init__(*args, **kwargs)
|
||||||
|
self.ws = None
|
||||||
|
self.data = ''
|
||||||
|
self.input_mode = False
|
||||||
|
|
||||||
|
|
||||||
|
class WebTerminalKillHandler(tornado.web.RequestHandler):
|
||||||
|
@require_auth('admin')
|
||||||
|
def get(self):
|
||||||
|
ws_id = self.get_argument('id')
|
||||||
|
Log.objects.filter(id=ws_id).update(is_finished=True)
|
||||||
|
for ws in WebTerminalHandler.clients:
|
||||||
|
if ws.id == int(ws_id):
|
||||||
|
logger.debug("Kill log id %s" % ws_id)
|
||||||
|
ws.log.save()
|
||||||
|
ws.close()
|
||||||
|
logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients))
|
||||||
|
|
||||||
|
|
||||||
|
class ExecHandler(tornado.websocket.WebSocketHandler):
|
||||||
|
clients = []
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.id = 0
|
||||||
|
self.user = None
|
||||||
|
self.role = None
|
||||||
|
self.runner = None
|
||||||
|
self.assets = []
|
||||||
|
self.perm = {}
|
||||||
|
self.remote_ip = ''
|
||||||
|
super(ExecHandler, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def check_origin(self, origin):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@require_auth('user')
|
||||||
|
def open(self):
|
||||||
|
logger.debug('Websocket: Open exec request')
|
||||||
|
role_name = self.get_argument('role', 'sb')
|
||||||
|
self.remote_ip = self.request.remote_ip
|
||||||
|
logger.debug('Web执行命令: 请求系统用户 %s' % role_name)
|
||||||
|
self.role = get_object(PermRole, name=role_name)
|
||||||
|
self.perm = get_group_user_perm(self.user)
|
||||||
|
roles = self.perm.get('role').keys()
|
||||||
|
if self.role not in roles:
|
||||||
|
self.write_message('No perm that role %s' % role_name)
|
||||||
|
self.close()
|
||||||
|
self.assets = self.perm.get('role').get(self.role).get('asset')
|
||||||
|
|
||||||
|
res = gen_resource({'user': self.user, 'asset': self.assets, 'role': self.role})
|
||||||
|
self.runner = MyRunner(res)
|
||||||
|
message = '有权限的主机: ' + ', '.join([asset.hostname for asset in self.assets])
|
||||||
|
self.__class__.clients.append(self)
|
||||||
|
self.write_message(message)
|
||||||
|
|
||||||
|
def on_message(self, message):
|
||||||
|
data = json.loads(message)
|
||||||
|
pattern = data.get('pattern', '')
|
||||||
|
command = data.get('command', '')
|
||||||
|
asset_name_str = ''
|
||||||
|
if pattern and command:
|
||||||
|
for inv in self.runner.inventory.get_hosts(pattern=pattern):
|
||||||
|
asset_name_str += '%s ' % inv.name
|
||||||
|
self.write_message('匹配主机: ' + asset_name_str)
|
||||||
|
self.write_message('<span style="color: yellow">Ansible> %s</span>\n\n' % command)
|
||||||
|
self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(command, pattern)))
|
||||||
|
ExecLog(host=asset_name_str, cmd=command, user=self.user.username, remote_ip=self.remote_ip).save()
|
||||||
|
|
||||||
|
for t in self.__class__.tasks:
|
||||||
|
if t.is_alive():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_cmd(self, command, pattern):
|
||||||
|
self.runner.run('shell', command, pattern=pattern)
|
||||||
|
newline_pattern = re.compile(r'\n')
|
||||||
|
for k, v in self.runner.results.items():
|
||||||
|
for host, output in v.items():
|
||||||
|
output = newline_pattern.sub('<br />', output)
|
||||||
|
logger.debug(output)
|
||||||
|
if k == 'ok':
|
||||||
|
header = "<span style='color: green'>[ %s => %s]</span>\n" % (host, 'Ok')
|
||||||
|
else:
|
||||||
|
header = "<span style='color: red'>[ %s => %s]</span>\n" % (host, 'failed')
|
||||||
|
self.write_message(header)
|
||||||
|
self.write_message(output)
|
||||||
|
|
||||||
|
self.write_message('\n~o~ Task finished ~o~\n')
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
logger.debug('关闭web_exec请求')
|
||||||
|
|
||||||
|
|
||||||
|
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||||
|
clients = []
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.term = None
|
||||||
|
self.log_file_f = None
|
||||||
|
self.log_time_f = None
|
||||||
|
self.log = None
|
||||||
|
self.id = 0
|
||||||
|
self.user = None
|
||||||
|
self.ssh = None
|
||||||
|
self.channel = None
|
||||||
|
super(WebTerminalHandler, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def check_origin(self, origin):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@require_auth('user')
|
||||||
|
def open(self):
|
||||||
|
logger.debug('Websocket: Open request')
|
||||||
|
role_name = self.get_argument('role', 'sb')
|
||||||
|
asset_id = self.get_argument('id', 9999)
|
||||||
|
asset = get_object(Asset, id=asset_id)
|
||||||
|
if asset:
|
||||||
|
roles = user_have_perm(self.user, asset)
|
||||||
|
logger.debug(roles)
|
||||||
|
logger.debug('系统用户: %s' % role_name)
|
||||||
|
login_role = ''
|
||||||
|
for role in roles:
|
||||||
|
if role.name == role_name:
|
||||||
|
login_role = role
|
||||||
|
break
|
||||||
|
if not login_role:
|
||||||
|
logger.warning('Websocket: Not that Role %s for Host: %s User: %s ' % (role_name, asset.hostname,
|
||||||
|
self.user.username))
|
||||||
|
self.close()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
logger.warning('Websocket: No that Host: %s User: %s ' % (asset_id, self.user.username))
|
||||||
|
self.close()
|
||||||
|
return
|
||||||
|
logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username,
|
||||||
|
login_role.name))
|
||||||
|
self.term = WebTty(self.user, asset, login_role, login_type='web')
|
||||||
|
self.term.remote_ip = self.request.remote_ip
|
||||||
|
self.ssh = self.term.get_connection()
|
||||||
|
self.channel = self.ssh.invoke_shell(term='xterm')
|
||||||
|
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
|
||||||
|
WebTerminalHandler.clients.append(self)
|
||||||
|
|
||||||
|
for t in WebTerminalHandler.tasks:
|
||||||
|
if t.is_alive():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_message(self, message):
|
||||||
|
data = json.loads(message)
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
if data.get('data'):
|
||||||
|
self.term.input_mode = True
|
||||||
|
if str(data['data']) in ['\r', '\n', '\r\n']:
|
||||||
|
if self.term.vim_flag:
|
||||||
|
match = self.term.ps1_pattern.search(self.term.vim_data)
|
||||||
|
if match:
|
||||||
|
self.term.vim_flag = False
|
||||||
|
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
|
||||||
|
if len(data) > 0:
|
||||||
|
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save()
|
||||||
|
|
||||||
|
TtyLog(log=self.log, datetime=datetime.datetime.now(),
|
||||||
|
cmd=self.term.deal_command(self.term.data)[0:200]).save()
|
||||||
|
self.term.vim_data = ''
|
||||||
|
self.term.data = ''
|
||||||
|
self.term.input_mode = False
|
||||||
|
self.channel.send(data['data'])
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
logger.debug('Websocket: Close request')
|
||||||
|
if self in WebTerminalHandler.clients:
|
||||||
|
WebTerminalHandler.clients.remove(self)
|
||||||
|
try:
|
||||||
|
self.log_file_f.write('End time is %s' % datetime.datetime.now())
|
||||||
|
self.log.is_finished = True
|
||||||
|
self.log.end_time = datetime.datetime.now()
|
||||||
|
self.log.save()
|
||||||
|
self.log_time_f.close()
|
||||||
|
print "1"
|
||||||
|
self.ssh.close()
|
||||||
|
print "2"
|
||||||
|
self.close()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forward_outbound(self):
|
||||||
|
self.log_file_f, self.log_time_f, self.log = self.term.get_log()
|
||||||
|
self.id = self.log.id
|
||||||
|
try:
|
||||||
|
data = ''
|
||||||
|
pre_timestamp = time.time()
|
||||||
|
while True:
|
||||||
|
r, w, e = select.select([self.channel, sys.stdin], [], [])
|
||||||
|
if self.channel in r:
|
||||||
|
recv = self.channel.recv(1024)
|
||||||
|
if not len(recv):
|
||||||
|
return
|
||||||
|
data += recv
|
||||||
|
if self.term.vim_flag:
|
||||||
|
self.term.vim_data += recv
|
||||||
|
try:
|
||||||
|
self.write_message(json.dumps({'data': data}))
|
||||||
|
now_timestamp = time.time()
|
||||||
|
self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
|
||||||
|
self.log_file_f.write(data)
|
||||||
|
pre_timestamp = now_timestamp
|
||||||
|
self.log_file_f.flush()
|
||||||
|
self.log_time_f.flush()
|
||||||
|
if self.term.input_mode and not self.term.is_output(data):
|
||||||
|
self.term.data += data
|
||||||
|
data = ''
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Application(tornado.web.Application):
|
||||||
|
def __init__(self):
|
||||||
|
handlers = [
|
||||||
|
(r'/monitor', MonitorHandler),
|
||||||
|
(r'/terminal', WebTerminalHandler),
|
||||||
|
(r'/kill', WebTerminalKillHandler),
|
||||||
|
(r'/exec', ExecHandler),
|
||||||
|
]
|
||||||
|
|
||||||
|
setting = {
|
||||||
|
'cookie_secret': 'DFksdfsasdfkasdfFKwlwfsdfsa1204mx',
|
||||||
|
'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
|
||||||
|
'static_path': os.path.join(os.path.dirname(__file__), 'static'),
|
||||||
|
'debug': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
tornado.web.Application.__init__(self, handlers, **setting)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tornado.options.parse_command_line()
|
||||||
|
app = Application()
|
||||||
|
server = tornado.httpserver.HTTPServer(app)
|
||||||
|
server.bind(options.port, options.host)
|
||||||
|
#server.listen(options.port)
|
||||||
|
server.start(num_processes=10)
|
||||||
|
print "Run server on %s:%s" % (options.host, options.port)
|
||||||
|
tornado.ioloop.IOLoop.instance().start()
|
|
@ -0,0 +1,689 @@
|
||||||
|
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
|
||||||
|
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
|
||||||
|
audio:not([controls]){display:none;}
|
||||||
|
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
|
||||||
|
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||||
|
a:hover,a:active{outline:0;}
|
||||||
|
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
|
||||||
|
sup{top:-0.5em;}
|
||||||
|
sub{bottom:-0.25em;}
|
||||||
|
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
|
||||||
|
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
|
||||||
|
button,input{*overflow:visible;line-height:normal;}
|
||||||
|
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
|
||||||
|
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
|
||||||
|
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
|
||||||
|
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
|
||||||
|
textarea{overflow:auto;vertical-align:top;}
|
||||||
|
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
|
||||||
|
.clearfix:after{clear:both;}
|
||||||
|
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
|
||||||
|
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
|
||||||
|
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
|
||||||
|
a{color:#0088cc;text-decoration:none;}
|
||||||
|
a:hover{color:#005580;text-decoration:underline;}
|
||||||
|
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
|
||||||
|
.row:after{clear:both;}
|
||||||
|
[class*="span"]{float:left;margin-left:20px;}
|
||||||
|
.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||||
|
.span12{width:940px;}
|
||||||
|
.span11{width:860px;}
|
||||||
|
.span10{width:780px;}
|
||||||
|
.span9{width:700px;}
|
||||||
|
.span8{width:620px;}
|
||||||
|
.span7{width:540px;}
|
||||||
|
.span6{width:460px;}
|
||||||
|
.span5{width:380px;}
|
||||||
|
.span4{width:300px;}
|
||||||
|
.span3{width:220px;}
|
||||||
|
.span2{width:140px;}
|
||||||
|
.span1{width:60px;}
|
||||||
|
.offset12{margin-left:980px;}
|
||||||
|
.offset11{margin-left:900px;}
|
||||||
|
.offset10{margin-left:820px;}
|
||||||
|
.offset9{margin-left:740px;}
|
||||||
|
.offset8{margin-left:660px;}
|
||||||
|
.offset7{margin-left:580px;}
|
||||||
|
.offset6{margin-left:500px;}
|
||||||
|
.offset5{margin-left:420px;}
|
||||||
|
.offset4{margin-left:340px;}
|
||||||
|
.offset3{margin-left:260px;}
|
||||||
|
.offset2{margin-left:180px;}
|
||||||
|
.offset1{margin-left:100px;}
|
||||||
|
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
|
||||||
|
.row-fluid:after{clear:both;}
|
||||||
|
.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
|
||||||
|
.row-fluid>[class*="span"]:first-child{margin-left:0;}
|
||||||
|
.row-fluid > .span12{width:99.99999998999999%;}
|
||||||
|
.row-fluid > .span11{width:91.489361693%;}
|
||||||
|
.row-fluid > .span10{width:82.97872339599999%;}
|
||||||
|
.row-fluid > .span9{width:74.468085099%;}
|
||||||
|
.row-fluid > .span8{width:65.95744680199999%;}
|
||||||
|
.row-fluid > .span7{width:57.446808505%;}
|
||||||
|
.row-fluid > .span6{width:48.93617020799999%;}
|
||||||
|
.row-fluid > .span5{width:40.425531911%;}
|
||||||
|
.row-fluid > .span4{width:31.914893614%;}
|
||||||
|
.row-fluid > .span3{width:23.404255317%;}
|
||||||
|
.row-fluid > .span2{width:14.89361702%;}
|
||||||
|
.row-fluid > .span1{width:6.382978723%;}
|
||||||
|
.container{margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
|
||||||
|
.container:after{clear:both;}
|
||||||
|
.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
|
||||||
|
.container-fluid:after{clear:both;}
|
||||||
|
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
|
||||||
|
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
|
||||||
|
h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
|
||||||
|
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
|
||||||
|
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
|
||||||
|
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
|
||||||
|
h4,h5,h6{line-height:18px;}
|
||||||
|
h4{font-size:14px;}h4 small{font-size:12px;}
|
||||||
|
h5{font-size:12px;}
|
||||||
|
h6{font-size:11px;color:#999999;text-transform:uppercase;}
|
||||||
|
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
|
||||||
|
.page-header h1{line-height:1;}
|
||||||
|
ul,ol{padding:0;margin:0 0 9px 25px;}
|
||||||
|
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
|
||||||
|
ul{list-style:disc;}
|
||||||
|
ol{list-style:decimal;}
|
||||||
|
li{line-height:18px;}
|
||||||
|
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
|
||||||
|
dl{margin-bottom:18px;}
|
||||||
|
dt,dd{line-height:18px;}
|
||||||
|
dt{font-weight:bold;line-height:17px;}
|
||||||
|
dd{margin-left:9px;}
|
||||||
|
.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;}
|
||||||
|
.dl-horizontal dd{margin-left:130px;}
|
||||||
|
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
|
||||||
|
strong{font-weight:bold;}
|
||||||
|
em{font-style:italic;}
|
||||||
|
.muted{color:#999999;}
|
||||||
|
abbr[title]{border-bottom:1px dotted #ddd;cursor:help;}
|
||||||
|
abbr.initialism{font-size:90%;text-transform:uppercase;}
|
||||||
|
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
|
||||||
|
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
|
||||||
|
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
|
||||||
|
q:before,q:after,blockquote:before,blockquote:after{content:"";}
|
||||||
|
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
|
||||||
|
small{font-size:100%;}
|
||||||
|
cite{font-style:normal;}
|
||||||
|
code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||||
|
code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
|
||||||
|
pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;}
|
||||||
|
pre code{padding:0;color:inherit;background-color:transparent;border:0;}
|
||||||
|
.pre-scrollable{max-height:340px;overflow-y:scroll;}
|
||||||
|
form{margin:0 0 18px;}
|
||||||
|
fieldset{padding:0;margin:0;border:0;}
|
||||||
|
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
|
||||||
|
label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
|
||||||
|
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
|
||||||
|
label{display:block;margin-bottom:5px;color:#333333;}
|
||||||
|
input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||||
|
.uneditable-textarea{width:auto;height:auto;}
|
||||||
|
label input,label textarea,label select{display:block;}
|
||||||
|
input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;}
|
||||||
|
input[type="image"]{border:0;}
|
||||||
|
input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||||
|
input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
|
||||||
|
select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
|
||||||
|
input[type="file"]{line-height:18px \9;}
|
||||||
|
select{width:220px;background-color:#ffffff;}
|
||||||
|
select[multiple],select[size]{height:auto;}
|
||||||
|
input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||||
|
textarea{height:auto;}
|
||||||
|
input[type="hidden"]{display:none;}
|
||||||
|
.radio,.checkbox{padding-left:18px;}
|
||||||
|
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
|
||||||
|
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
|
||||||
|
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
|
||||||
|
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
|
||||||
|
input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
|
||||||
|
input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
|
||||||
|
input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||||
|
.input-mini{width:60px;}
|
||||||
|
.input-small{width:90px;}
|
||||||
|
.input-medium{width:150px;}
|
||||||
|
.input-large{width:210px;}
|
||||||
|
.input-xlarge{width:270px;}
|
||||||
|
.input-xxlarge{width:530px;}
|
||||||
|
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
|
||||||
|
input,textarea,.uneditable-input{margin-left:0;}
|
||||||
|
input.span12, textarea.span12, .uneditable-input.span12{width:930px;}
|
||||||
|
input.span11, textarea.span11, .uneditable-input.span11{width:850px;}
|
||||||
|
input.span10, textarea.span10, .uneditable-input.span10{width:770px;}
|
||||||
|
input.span9, textarea.span9, .uneditable-input.span9{width:690px;}
|
||||||
|
input.span8, textarea.span8, .uneditable-input.span8{width:610px;}
|
||||||
|
input.span7, textarea.span7, .uneditable-input.span7{width:530px;}
|
||||||
|
input.span6, textarea.span6, .uneditable-input.span6{width:450px;}
|
||||||
|
input.span5, textarea.span5, .uneditable-input.span5{width:370px;}
|
||||||
|
input.span4, textarea.span4, .uneditable-input.span4{width:290px;}
|
||||||
|
input.span3, textarea.span3, .uneditable-input.span3{width:210px;}
|
||||||
|
input.span2, textarea.span2, .uneditable-input.span2{width:130px;}
|
||||||
|
input.span1, textarea.span1, .uneditable-input.span1{width:50px;}
|
||||||
|
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#eeeeee;border-color:#ddd;cursor:not-allowed;}
|
||||||
|
.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
|
||||||
|
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
|
||||||
|
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
|
||||||
|
.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
|
||||||
|
.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
|
||||||
|
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
|
||||||
|
.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
|
||||||
|
.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
|
||||||
|
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
|
||||||
|
input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
|
||||||
|
.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#eeeeee;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";}
|
||||||
|
.form-actions:after{clear:both;}
|
||||||
|
.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
|
||||||
|
:-moz-placeholder{color:#999999;}
|
||||||
|
::-webkit-input-placeholder{color:#999999;}
|
||||||
|
.help-block,.help-inline{color:#555555;}
|
||||||
|
.help-block{display:block;margin-bottom:9px;}
|
||||||
|
.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
|
||||||
|
.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{*margin-left:0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
|
||||||
|
.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
|
||||||
|
.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
|
||||||
|
.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||||
|
.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
|
||||||
|
.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
|
||||||
|
.input-append input,.input-append select .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||||
|
.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;}
|
||||||
|
.input-append .add-on,.input-append .btn{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||||
|
.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||||
|
.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||||
|
.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||||
|
.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
|
||||||
|
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;margin-bottom:0;}
|
||||||
|
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
|
||||||
|
.form-search label,.form-inline label{display:inline-block;}
|
||||||
|
.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
|
||||||
|
.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
|
||||||
|
.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px;}
|
||||||
|
.control-group{margin-bottom:9px;}
|
||||||
|
legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
|
||||||
|
.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
|
||||||
|
.form-horizontal .control-group:after{clear:both;}
|
||||||
|
.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
|
||||||
|
.form-horizontal .controls{margin-left:160px;*display:inline-block;*margin-left:0;*padding-left:20px;}
|
||||||
|
.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
|
||||||
|
.form-horizontal .form-actions{padding-left:160px;}
|
||||||
|
table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;}
|
||||||
|
.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
|
||||||
|
.table th{font-weight:bold;}
|
||||||
|
.table thead th{vertical-align:bottom;}
|
||||||
|
.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
|
||||||
|
.table tbody+tbody{border-top:2px solid #dddddd;}
|
||||||
|
.table-condensed th,.table-condensed td{padding:4px 5px;}
|
||||||
|
.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
|
||||||
|
.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
|
||||||
|
.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
|
||||||
|
.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
|
||||||
|
.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
|
||||||
|
.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
|
||||||
|
.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
|
||||||
|
.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
|
||||||
|
table .span1{float:none;width:44px;margin-left:0;}
|
||||||
|
table .span2{float:none;width:124px;margin-left:0;}
|
||||||
|
table .span3{float:none;width:204px;margin-left:0;}
|
||||||
|
table .span4{float:none;width:284px;margin-left:0;}
|
||||||
|
table .span5{float:none;width:364px;margin-left:0;}
|
||||||
|
table .span6{float:none;width:444px;margin-left:0;}
|
||||||
|
table .span7{float:none;width:524px;margin-left:0;}
|
||||||
|
table .span8{float:none;width:604px;margin-left:0;}
|
||||||
|
table .span9{float:none;width:684px;margin-left:0;}
|
||||||
|
table .span10{float:none;width:764px;margin-left:0;}
|
||||||
|
table .span11{float:none;width:844px;margin-left:0;}
|
||||||
|
table .span12{float:none;width:924px;margin-left:0;}
|
||||||
|
table .span13{float:none;width:1004px;margin-left:0;}
|
||||||
|
table .span14{float:none;width:1084px;margin-left:0;}
|
||||||
|
table .span15{float:none;width:1164px;margin-left:0;}
|
||||||
|
table .span16{float:none;width:1244px;margin-left:0;}
|
||||||
|
table .span17{float:none;width:1324px;margin-left:0;}
|
||||||
|
table .span18{float:none;width:1404px;margin-left:0;}
|
||||||
|
table .span19{float:none;width:1484px;margin-left:0;}
|
||||||
|
table .span20{float:none;width:1564px;margin-left:0;}
|
||||||
|
table .span21{float:none;width:1644px;margin-left:0;}
|
||||||
|
table .span22{float:none;width:1724px;margin-left:0;}
|
||||||
|
table .span23{float:none;width:1804px;margin-left:0;}
|
||||||
|
table .span24{float:none;width:1884px;margin-left:0;}
|
||||||
|
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
|
||||||
|
.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
|
||||||
|
.icon-glass{background-position:0 0;}
|
||||||
|
.icon-music{background-position:-24px 0;}
|
||||||
|
.icon-search{background-position:-48px 0;}
|
||||||
|
.icon-envelope{background-position:-72px 0;}
|
||||||
|
.icon-heart{background-position:-96px 0;}
|
||||||
|
.icon-star{background-position:-120px 0;}
|
||||||
|
.icon-star-empty{background-position:-144px 0;}
|
||||||
|
.icon-user{background-position:-168px 0;}
|
||||||
|
.icon-film{background-position:-192px 0;}
|
||||||
|
.icon-th-large{background-position:-216px 0;}
|
||||||
|
.icon-th{background-position:-240px 0;}
|
||||||
|
.icon-th-list{background-position:-264px 0;}
|
||||||
|
.icon-ok{background-position:-288px 0;}
|
||||||
|
.icon-remove{background-position:-312px 0;}
|
||||||
|
.icon-zoom-in{background-position:-336px 0;}
|
||||||
|
.icon-zoom-out{background-position:-360px 0;}
|
||||||
|
.icon-off{background-position:-384px 0;}
|
||||||
|
.icon-signal{background-position:-408px 0;}
|
||||||
|
.icon-cog{background-position:-432px 0;}
|
||||||
|
.icon-trash{background-position:-456px 0;}
|
||||||
|
.icon-home{background-position:0 -24px;}
|
||||||
|
.icon-file{background-position:-24px -24px;}
|
||||||
|
.icon-time{background-position:-48px -24px;}
|
||||||
|
.icon-road{background-position:-72px -24px;}
|
||||||
|
.icon-download-alt{background-position:-96px -24px;}
|
||||||
|
.icon-download{background-position:-120px -24px;}
|
||||||
|
.icon-upload{background-position:-144px -24px;}
|
||||||
|
.icon-inbox{background-position:-168px -24px;}
|
||||||
|
.icon-play-circle{background-position:-192px -24px;}
|
||||||
|
.icon-repeat{background-position:-216px -24px;}
|
||||||
|
.icon-refresh{background-position:-240px -24px;}
|
||||||
|
.icon-list-alt{background-position:-264px -24px;}
|
||||||
|
.icon-lock{background-position:-287px -24px;}
|
||||||
|
.icon-flag{background-position:-312px -24px;}
|
||||||
|
.icon-headphones{background-position:-336px -24px;}
|
||||||
|
.icon-volume-off{background-position:-360px -24px;}
|
||||||
|
.icon-volume-down{background-position:-384px -24px;}
|
||||||
|
.icon-volume-up{background-position:-408px -24px;}
|
||||||
|
.icon-qrcode{background-position:-432px -24px;}
|
||||||
|
.icon-barcode{background-position:-456px -24px;}
|
||||||
|
.icon-tag{background-position:0 -48px;}
|
||||||
|
.icon-tags{background-position:-25px -48px;}
|
||||||
|
.icon-book{background-position:-48px -48px;}
|
||||||
|
.icon-bookmark{background-position:-72px -48px;}
|
||||||
|
.icon-print{background-position:-96px -48px;}
|
||||||
|
.icon-camera{background-position:-120px -48px;}
|
||||||
|
.icon-font{background-position:-144px -48px;}
|
||||||
|
.icon-bold{background-position:-167px -48px;}
|
||||||
|
.icon-italic{background-position:-192px -48px;}
|
||||||
|
.icon-text-height{background-position:-216px -48px;}
|
||||||
|
.icon-text-width{background-position:-240px -48px;}
|
||||||
|
.icon-align-left{background-position:-264px -48px;}
|
||||||
|
.icon-align-center{background-position:-288px -48px;}
|
||||||
|
.icon-align-right{background-position:-312px -48px;}
|
||||||
|
.icon-align-justify{background-position:-336px -48px;}
|
||||||
|
.icon-list{background-position:-360px -48px;}
|
||||||
|
.icon-indent-left{background-position:-384px -48px;}
|
||||||
|
.icon-indent-right{background-position:-408px -48px;}
|
||||||
|
.icon-facetime-video{background-position:-432px -48px;}
|
||||||
|
.icon-picture{background-position:-456px -48px;}
|
||||||
|
.icon-pencil{background-position:0 -72px;}
|
||||||
|
.icon-map-marker{background-position:-24px -72px;}
|
||||||
|
.icon-adjust{background-position:-48px -72px;}
|
||||||
|
.icon-tint{background-position:-72px -72px;}
|
||||||
|
.icon-edit{background-position:-96px -72px;}
|
||||||
|
.icon-share{background-position:-120px -72px;}
|
||||||
|
.icon-check{background-position:-144px -72px;}
|
||||||
|
.icon-move{background-position:-168px -72px;}
|
||||||
|
.icon-step-backward{background-position:-192px -72px;}
|
||||||
|
.icon-fast-backward{background-position:-216px -72px;}
|
||||||
|
.icon-backward{background-position:-240px -72px;}
|
||||||
|
.icon-play{background-position:-264px -72px;}
|
||||||
|
.icon-pause{background-position:-288px -72px;}
|
||||||
|
.icon-stop{background-position:-312px -72px;}
|
||||||
|
.icon-forward{background-position:-336px -72px;}
|
||||||
|
.icon-fast-forward{background-position:-360px -72px;}
|
||||||
|
.icon-step-forward{background-position:-384px -72px;}
|
||||||
|
.icon-eject{background-position:-408px -72px;}
|
||||||
|
.icon-chevron-left{background-position:-432px -72px;}
|
||||||
|
.icon-chevron-right{background-position:-456px -72px;}
|
||||||
|
.icon-plus-sign{background-position:0 -96px;}
|
||||||
|
.icon-minus-sign{background-position:-24px -96px;}
|
||||||
|
.icon-remove-sign{background-position:-48px -96px;}
|
||||||
|
.icon-ok-sign{background-position:-72px -96px;}
|
||||||
|
.icon-question-sign{background-position:-96px -96px;}
|
||||||
|
.icon-info-sign{background-position:-120px -96px;}
|
||||||
|
.icon-screenshot{background-position:-144px -96px;}
|
||||||
|
.icon-remove-circle{background-position:-168px -96px;}
|
||||||
|
.icon-ok-circle{background-position:-192px -96px;}
|
||||||
|
.icon-ban-circle{background-position:-216px -96px;}
|
||||||
|
.icon-arrow-left{background-position:-240px -96px;}
|
||||||
|
.icon-arrow-right{background-position:-264px -96px;}
|
||||||
|
.icon-arrow-up{background-position:-289px -96px;}
|
||||||
|
.icon-arrow-down{background-position:-312px -96px;}
|
||||||
|
.icon-share-alt{background-position:-336px -96px;}
|
||||||
|
.icon-resize-full{background-position:-360px -96px;}
|
||||||
|
.icon-resize-small{background-position:-384px -96px;}
|
||||||
|
.icon-plus{background-position:-408px -96px;}
|
||||||
|
.icon-minus{background-position:-433px -96px;}
|
||||||
|
.icon-asterisk{background-position:-456px -96px;}
|
||||||
|
.icon-exclamation-sign{background-position:0 -120px;}
|
||||||
|
.icon-gift{background-position:-24px -120px;}
|
||||||
|
.icon-leaf{background-position:-48px -120px;}
|
||||||
|
.icon-fire{background-position:-72px -120px;}
|
||||||
|
.icon-eye-open{background-position:-96px -120px;}
|
||||||
|
.icon-eye-close{background-position:-120px -120px;}
|
||||||
|
.icon-warning-sign{background-position:-144px -120px;}
|
||||||
|
.icon-plane{background-position:-168px -120px;}
|
||||||
|
.icon-calendar{background-position:-192px -120px;}
|
||||||
|
.icon-random{background-position:-216px -120px;}
|
||||||
|
.icon-comment{background-position:-240px -120px;}
|
||||||
|
.icon-magnet{background-position:-264px -120px;}
|
||||||
|
.icon-chevron-up{background-position:-288px -120px;}
|
||||||
|
.icon-chevron-down{background-position:-313px -119px;}
|
||||||
|
.icon-retweet{background-position:-336px -120px;}
|
||||||
|
.icon-shopping-cart{background-position:-360px -120px;}
|
||||||
|
.icon-folder-close{background-position:-384px -120px;}
|
||||||
|
.icon-folder-open{background-position:-408px -120px;}
|
||||||
|
.icon-resize-vertical{background-position:-432px -119px;}
|
||||||
|
.icon-resize-horizontal{background-position:-456px -118px;}
|
||||||
|
.dropdown{position:relative;}
|
||||||
|
.dropdown-toggle{*margin-bottom:-3px;}
|
||||||
|
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
|
||||||
|
.caret{display:inline-block;width:0;height:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"";}
|
||||||
|
.dropdown .caret{margin-top:8px;margin-left:2px;}
|
||||||
|
.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
|
||||||
|
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.pull-right{right:0;left:auto;}
|
||||||
|
.dropdown-menu .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||||
|
.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
|
||||||
|
.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
|
||||||
|
.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
|
||||||
|
.dropdown.open .dropdown-menu{display:block;}
|
||||||
|
.pull-right .dropdown-menu{left:auto;right:0;}
|
||||||
|
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
|
||||||
|
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
|
||||||
|
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||||
|
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
|
||||||
|
.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
|
||||||
|
.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||||
|
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
|
||||||
|
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
|
||||||
|
.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
|
||||||
|
.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #cccccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;}
|
||||||
|
.btn:active,.btn.active{background-color:#cccccc \9;}
|
||||||
|
.btn:first-child{*margin-left:0;}
|
||||||
|
.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
|
||||||
|
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||||
|
.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;}
|
||||||
|
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||||
|
.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||||
|
.btn-large [class^="icon-"]{margin-top:1px;}
|
||||||
|
.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
|
||||||
|
.btn-small [class^="icon-"]{margin-top:-1px;}
|
||||||
|
.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
|
||||||
|
.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
|
||||||
|
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
|
||||||
|
.btn-primary{background-color:#0074cc;background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;}
|
||||||
|
.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
|
||||||
|
.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
|
||||||
|
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
|
||||||
|
.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
|
||||||
|
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
|
||||||
|
.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
|
||||||
|
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
|
||||||
|
.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
|
||||||
|
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
|
||||||
|
.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;}
|
||||||
|
.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
|
||||||
|
button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
|
||||||
|
button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
|
||||||
|
button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
|
||||||
|
button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
|
||||||
|
.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
|
||||||
|
.btn-group:after{clear:both;}
|
||||||
|
.btn-group:first-child{*margin-left:0;}
|
||||||
|
.btn-group+.btn-group{margin-left:5px;}
|
||||||
|
.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
|
||||||
|
.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||||
|
.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
|
||||||
|
.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
|
||||||
|
.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
|
||||||
|
.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
|
||||||
|
.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
|
||||||
|
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
|
||||||
|
.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:3px;*padding-bottom:3px;}
|
||||||
|
.btn-group .btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:1px;*padding-bottom:1px;}
|
||||||
|
.btn-group .btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
|
||||||
|
.btn-group .btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;}
|
||||||
|
.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||||
|
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||||
|
.btn .caret{margin-top:7px;margin-left:0;}
|
||||||
|
.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
|
||||||
|
.btn-mini .caret{margin-top:5px;}
|
||||||
|
.btn-small .caret{margin-top:6px;}
|
||||||
|
.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||||
|
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
|
||||||
|
.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
|
||||||
|
.alert-heading{color:inherit;}
|
||||||
|
.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
|
||||||
|
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
|
||||||
|
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
|
||||||
|
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
|
||||||
|
.alert-block{padding-top:14px;padding-bottom:14px;}
|
||||||
|
.alert-block>p,.alert-block>ul{margin-bottom:0;}
|
||||||
|
.alert-block p+p{margin-top:5px;}
|
||||||
|
.nav{margin-left:0;margin-bottom:18px;list-style:none;}
|
||||||
|
.nav>li>a{display:block;}
|
||||||
|
.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
|
||||||
|
.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
|
||||||
|
.nav li+.nav-header{margin-top:9px;}
|
||||||
|
.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
|
||||||
|
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
|
||||||
|
.nav-list>li>a{padding:3px 15px;}
|
||||||
|
.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
|
||||||
|
.nav-list [class^="icon-"]{margin-right:2px;}
|
||||||
|
.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||||
|
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
|
||||||
|
.nav-tabs:after,.nav-pills:after{clear:both;}
|
||||||
|
.nav-tabs>li,.nav-pills>li{float:left;}
|
||||||
|
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
|
||||||
|
.nav-tabs{border-bottom:1px solid #ddd;}
|
||||||
|
.nav-tabs>li{margin-bottom:-1px;}
|
||||||
|
.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
|
||||||
|
.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
|
||||||
|
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||||
|
.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
|
||||||
|
.nav-stacked>li{float:none;}
|
||||||
|
.nav-stacked>li>a{margin-right:0;}
|
||||||
|
.nav-tabs.nav-stacked{border-bottom:0;}
|
||||||
|
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||||
|
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
|
||||||
|
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
|
||||||
|
.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
|
||||||
|
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
|
||||||
|
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
|
||||||
|
.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
|
||||||
|
.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||||
|
.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
|
||||||
|
.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
|
||||||
|
.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
|
||||||
|
.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
|
||||||
|
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
|
||||||
|
.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
|
||||||
|
.tabs-stacked .open>a:hover{border-color:#999999;}
|
||||||
|
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
|
||||||
|
.tabbable:after{clear:both;}
|
||||||
|
.tab-content{display:table;width:100%;}
|
||||||
|
.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
|
||||||
|
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
|
||||||
|
.tab-content>.active,.pill-content>.active{display:block;}
|
||||||
|
.tabs-below .nav-tabs{border-top:1px solid #ddd;}
|
||||||
|
.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
|
||||||
|
.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
|
||||||
|
.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
|
||||||
|
.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
|
||||||
|
.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
|
||||||
|
.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
|
||||||
|
.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
|
||||||
|
.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
|
||||||
|
.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
|
||||||
|
.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
|
||||||
|
.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
|
||||||
|
.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
|
||||||
|
.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
|
||||||
|
.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;}
|
||||||
|
.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
|
||||||
|
.navbar .container{width:auto;}
|
||||||
|
.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
|
||||||
|
.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
|
||||||
|
.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
|
||||||
|
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
|
||||||
|
.nav-collapse.collapse{height:auto;}
|
||||||
|
.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;}
|
||||||
|
.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
|
||||||
|
.navbar .navbar-text{margin-bottom:0;line-height:40px;}
|
||||||
|
.navbar .btn,.navbar .btn-group{margin-top:5px;}
|
||||||
|
.navbar .btn-group .btn{margin-top:0;}
|
||||||
|
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
|
||||||
|
.navbar-form:after{clear:both;}
|
||||||
|
.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
|
||||||
|
.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
|
||||||
|
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
|
||||||
|
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
|
||||||
|
.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
|
||||||
|
.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
|
||||||
|
.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
|
||||||
|
.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
|
||||||
|
.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||||
|
.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||||
|
.navbar-fixed-top{top:0;}
|
||||||
|
.navbar-fixed-bottom{bottom:0;}
|
||||||
|
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
|
||||||
|
.navbar .nav.pull-right{float:right;}
|
||||||
|
.navbar .nav>li{display:block;float:left;}
|
||||||
|
.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
|
||||||
|
.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
|
||||||
|
.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
|
||||||
|
.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
|
||||||
|
.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
|
||||||
|
.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
|
||||||
|
.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
|
||||||
|
.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
|
||||||
|
.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
|
||||||
|
.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
|
||||||
|
.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
|
||||||
|
.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
|
||||||
|
.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
|
||||||
|
.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;}
|
||||||
|
.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;}
|
||||||
|
.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
|
||||||
|
.breadcrumb .divider{padding:0 5px;color:#999999;}
|
||||||
|
.breadcrumb .active a{color:#333333;}
|
||||||
|
.pagination{height:36px;margin:18px 0;}
|
||||||
|
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||||
|
.pagination li{display:inline;}
|
||||||
|
.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
|
||||||
|
.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
|
||||||
|
.pagination .active a{color:#999999;cursor:default;}
|
||||||
|
.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
|
||||||
|
.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||||
|
.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||||
|
.pagination-centered{text-align:center;}
|
||||||
|
.pagination-right{text-align:right;}
|
||||||
|
.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
|
||||||
|
.pager:after{clear:both;}
|
||||||
|
.pager li{display:inline;}
|
||||||
|
.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
|
||||||
|
.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
|
||||||
|
.pager .next a{float:right;}
|
||||||
|
.pager .previous a{float:left;}
|
||||||
|
.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;}
|
||||||
|
.modal-open .dropdown-menu{z-index:2050;}
|
||||||
|
.modal-open .dropdown.open{*z-index:2050;}
|
||||||
|
.modal-open .popover{z-index:2060;}
|
||||||
|
.modal-open .tooltip{z-index:2070;}
|
||||||
|
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
|
||||||
|
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||||
|
.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
|
||||||
|
.modal.fade.in{top:50%;}
|
||||||
|
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
|
||||||
|
.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
|
||||||
|
.modal-form{margin-bottom:0;}
|
||||||
|
.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
|
||||||
|
.modal-footer:after{clear:both;}
|
||||||
|
.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
|
||||||
|
.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
|
||||||
|
.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||||
|
.tooltip.top{margin-top:-2px;}
|
||||||
|
.tooltip.right{margin-left:2px;}
|
||||||
|
.tooltip.bottom{margin-top:2px;}
|
||||||
|
.tooltip.left{margin-left:-2px;}
|
||||||
|
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||||
|
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||||
|
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||||
|
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||||
|
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||||
|
.tooltip-arrow{position:absolute;width:0;height:0;}
|
||||||
|
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
|
||||||
|
.popover.right{margin-left:5px;}
|
||||||
|
.popover.bottom{margin-top:5px;}
|
||||||
|
.popover.left{margin-left:-5px;}
|
||||||
|
.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||||
|
.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||||
|
.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||||
|
.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||||
|
.popover .arrow{position:absolute;width:0;height:0;}
|
||||||
|
.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
|
||||||
|
.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
|
||||||
|
.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
|
||||||
|
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
|
||||||
|
.thumbnails:after{clear:both;}
|
||||||
|
.thumbnails>li{float:left;margin:0 0 18px 20px;}
|
||||||
|
.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
|
||||||
|
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
|
||||||
|
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
|
||||||
|
.thumbnail .caption{padding:9px;}
|
||||||
|
.label{padding:1px 4px 2px;font-size:10.998px;font-weight:bold;line-height:13px;color:#ffffff;vertical-align:middle;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||||
|
.label:hover{color:#ffffff;text-decoration:none;}
|
||||||
|
.label-important{background-color:#b94a48;}
|
||||||
|
.label-important:hover{background-color:#953b39;}
|
||||||
|
.label-warning{background-color:#f89406;}
|
||||||
|
.label-warning:hover{background-color:#c67605;}
|
||||||
|
.label-success{background-color:#468847;}
|
||||||
|
.label-success:hover{background-color:#356635;}
|
||||||
|
.label-info{background-color:#3a87ad;}
|
||||||
|
.label-info:hover{background-color:#2d6987;}
|
||||||
|
.label-inverse{background-color:#333333;}
|
||||||
|
.label-inverse:hover{background-color:#1a1a1a;}
|
||||||
|
.badge{padding:1px 9px 2px;font-size:12.025px;font-weight:bold;white-space:nowrap;color:#ffffff;background-color:#999999;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
|
||||||
|
.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
|
||||||
|
.badge-error{background-color:#b94a48;}
|
||||||
|
.badge-error:hover{background-color:#953b39;}
|
||||||
|
.badge-warning{background-color:#f89406;}
|
||||||
|
.badge-warning:hover{background-color:#c67605;}
|
||||||
|
.badge-success{background-color:#468847;}
|
||||||
|
.badge-success:hover{background-color:#356635;}
|
||||||
|
.badge-info{background-color:#3a87ad;}
|
||||||
|
.badge-info:hover{background-color:#2d6987;}
|
||||||
|
.badge-inverse{background-color:#333333;}
|
||||||
|
.badge-inverse:hover{background-color:#1a1a1a;}
|
||||||
|
@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||||
|
.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
|
||||||
|
.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
|
||||||
|
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
|
||||||
|
.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
|
||||||
|
.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||||
|
.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
|
||||||
|
.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||||
|
.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
|
||||||
|
.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||||
|
.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
|
||||||
|
.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||||
|
.accordion{margin-bottom:18px;}
|
||||||
|
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||||
|
.accordion-heading{border-bottom:0;}
|
||||||
|
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
|
||||||
|
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
|
||||||
|
.carousel{position:relative;margin-bottom:18px;line-height:1;}
|
||||||
|
.carousel-inner{overflow:hidden;width:100%;position:relative;}
|
||||||
|
.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
|
||||||
|
.carousel .item>img{display:block;line-height:1;}
|
||||||
|
.carousel .active,.carousel .next,.carousel .prev{display:block;}
|
||||||
|
.carousel .active{left:0;}
|
||||||
|
.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
|
||||||
|
.carousel .next{left:100%;}
|
||||||
|
.carousel .prev{left:-100%;}
|
||||||
|
.carousel .next.left,.carousel .prev.right{left:0;}
|
||||||
|
.carousel .active.left{left:-100%;}
|
||||||
|
.carousel .active.right{left:100%;}
|
||||||
|
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
|
||||||
|
.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
|
||||||
|
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
|
||||||
|
.carousel-caption h4,.carousel-caption p{color:#ffffff;}
|
||||||
|
.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
|
||||||
|
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
|
||||||
|
.pull-right{float:right;}
|
||||||
|
.pull-left{float:left;}
|
||||||
|
.hide{display:none;}
|
||||||
|
.show{display:block;}
|
||||||
|
.invisible{visibility:hidden;}
|
After Width: | Height: | Size: 646 B |
After Width: | Height: | Size: 872 B |
|
@ -0,0 +1,429 @@
|
||||||
|
/*!
|
||||||
|
Chosen, a Select Box Enhancer for jQuery and Prototype
|
||||||
|
by Patrick Filler for Harvest, http://getharvest.com
|
||||||
|
|
||||||
|
Version 1.1.0
|
||||||
|
Full source at https://github.com/harvesthq/chosen
|
||||||
|
Copyright (c) 2011 Harvest http://getharvest.com
|
||||||
|
|
||||||
|
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
|
||||||
|
This file is generated by `grunt build`, do not edit it by hand.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @group Base */
|
||||||
|
.chosen-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 13px;
|
||||||
|
zoom: 1;
|
||||||
|
*display: inline;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-drop {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: -9999px;
|
||||||
|
z-index: 1010;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-top: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
.chosen-container.chosen-with-drop .chosen-drop {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.chosen-container a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Single Chosen */
|
||||||
|
.chosen-container-single .chosen-single {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
height: 23px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
|
||||||
|
background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||||
|
background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||||
|
background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||||
|
background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||||
|
background-clip: padding-box;
|
||||||
|
box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
color: #444;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-default {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single span {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-right: 26px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single-with-deselect span {
|
||||||
|
margin-right: 38px;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single abbr {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 26px;
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: url('chosen-sprite.png') -42px 1px no-repeat;
|
||||||
|
font-size: 1px;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single abbr:hover {
|
||||||
|
background-position: -42px -10px;
|
||||||
|
}
|
||||||
|
.chosen-container-single.chosen-disabled .chosen-single abbr:hover {
|
||||||
|
background-position: -42px -10px;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
display: block;
|
||||||
|
width: 18px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-single div b {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url('chosen-sprite.png') no-repeat 0px 2px;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-search {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1010;
|
||||||
|
margin: 0;
|
||||||
|
padding: 3px 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-search input[type="text"] {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 1px 0;
|
||||||
|
padding: 4px 20px 4px 5px;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
outline: 0;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
background: white url('chosen-sprite.png') no-repeat 100% -20px;
|
||||||
|
background: url('chosen-sprite.png') no-repeat 100% -20px;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: normal;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.chosen-container-single .chosen-drop {
|
||||||
|
margin-top: -1px;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
.chosen-container-single.chosen-container-single-nosearch .chosen-search {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Results */
|
||||||
|
.chosen-container .chosen-results {
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin: 0 4px 4px 0;
|
||||||
|
padding: 0 0 0 4px;
|
||||||
|
max-height: 240px;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li {
|
||||||
|
display: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 6px;
|
||||||
|
list-style: none;
|
||||||
|
line-height: 15px;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.active-result {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.disabled-result {
|
||||||
|
display: list-item;
|
||||||
|
color: #ccc;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.highlighted {
|
||||||
|
background-color: #3875d7;
|
||||||
|
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
|
||||||
|
background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||||
|
background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||||
|
background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||||
|
background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.no-results {
|
||||||
|
display: list-item;
|
||||||
|
background: #f4f4f4;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.group-result {
|
||||||
|
display: list-item;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li.group-option {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
.chosen-container .chosen-results li em {
|
||||||
|
font-style: normal;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Multi Chosen */
|
||||||
|
.chosen-container-multi .chosen-choices {
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 1px solid #CBD5DD;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: text;
|
||||||
|
height: auto !important;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li {
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-field {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-field input[type="text"] {
|
||||||
|
margin: 1px 0;
|
||||||
|
padding: 5px;
|
||||||
|
height: 25px;
|
||||||
|
outline: 0;
|
||||||
|
border: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #666;
|
||||||
|
font-size: 100%;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: normal;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-field .default {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice {
|
||||||
|
position: relative;
|
||||||
|
margin: 3px 0 3px 5px;
|
||||||
|
padding: 3px 20px 3px 5px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
||||||
|
background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-clip: padding-box;
|
||||||
|
box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
color: #333;
|
||||||
|
line-height: 13px;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 3px;
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: url('chosen-sprite.png') -42px 1px no-repeat;
|
||||||
|
font-size: 1px;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover {
|
||||||
|
background-position: -42px -10px;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice-disabled {
|
||||||
|
padding-right: 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
||||||
|
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice-focus {
|
||||||
|
background: #d4d4d4;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close {
|
||||||
|
background-position: -42px -10px;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-results {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.chosen-container-multi .chosen-drop .result-selected {
|
||||||
|
display: list-item;
|
||||||
|
color: #ccc;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Active */
|
||||||
|
.chosen-container-active .chosen-single {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.chosen-container-active.chosen-with-drop .chosen-single {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
-moz-border-radius-bottomright: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
-moz-border-radius-bottomleft: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
|
||||||
|
background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||||
|
background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||||
|
background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||||
|
background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||||
|
box-shadow: 0 1px 0 #fff inset;
|
||||||
|
}
|
||||||
|
.chosen-container-active.chosen-with-drop .chosen-single div {
|
||||||
|
border-left: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.chosen-container-active.chosen-with-drop .chosen-single div b {
|
||||||
|
background-position: -18px 2px;
|
||||||
|
}
|
||||||
|
.chosen-container-active .chosen-choices {
|
||||||
|
border: 1px solid #5897fb;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.chosen-container-active .chosen-choices li.search-field input[type="text"] {
|
||||||
|
color: #111 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Disabled Support */
|
||||||
|
.chosen-disabled {
|
||||||
|
opacity: 0.5 !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.chosen-disabled .chosen-single {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.chosen-disabled .chosen-choices .search-choice .search-choice-close {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Right to Left */
|
||||||
|
.chosen-rtl {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-single {
|
||||||
|
overflow: visible;
|
||||||
|
padding: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-single span {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 26px;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-single-with-deselect span {
|
||||||
|
margin-left: 38px;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-single div {
|
||||||
|
right: auto;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-single abbr {
|
||||||
|
right: auto;
|
||||||
|
left: 26px;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-choices li {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-choices li.search-field input[type="text"] {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-choices li.search-choice {
|
||||||
|
margin: 3px 5px 3px 0;
|
||||||
|
padding: 3px 5px 3px 19px;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-choices li.search-choice .search-choice-close {
|
||||||
|
right: auto;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
|
.chosen-rtl.chosen-container-single-nosearch .chosen-search,
|
||||||
|
.chosen-rtl .chosen-drop {
|
||||||
|
left: 9999px;
|
||||||
|
}
|
||||||
|
.chosen-rtl.chosen-container-single .chosen-results {
|
||||||
|
margin: 0 0 4px 4px;
|
||||||
|
padding: 0 4px 0 0;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-results li.group-option {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.chosen-rtl .chosen-search input[type="text"] {
|
||||||
|
padding: 4px 5px 4px 20px;
|
||||||
|
background: white url('chosen-sprite.png') no-repeat -30px -20px;
|
||||||
|
background: url('chosen-sprite.png') no-repeat -30px -20px;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.chosen-rtl.chosen-container-single .chosen-single div b {
|
||||||
|
background-position: 6px 2px;
|
||||||
|
}
|
||||||
|
.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b {
|
||||||
|
background-position: -12px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @end */
|
||||||
|
/* @group Retina compatibility */
|
||||||
|
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) {
|
||||||
|
.chosen-rtl .chosen-search input[type="text"],
|
||||||
|
.chosen-container-single .chosen-single abbr,
|
||||||
|
.chosen-container-single .chosen-single div b,
|
||||||
|
.chosen-container-single .chosen-search input[type="text"],
|
||||||
|
.chosen-container-multi .chosen-choices .search-choice .search-choice-close,
|
||||||
|
.chosen-container .chosen-results-scroll-down span,
|
||||||
|
.chosen-container .chosen-results-scroll-up span {
|
||||||
|
background-image: url('chosen-sprite@2x.png') !important;
|
||||||
|
background-size: 52px 37px !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* @end */
|
|
@ -0,0 +1,789 @@
|
||||||
|
/*!
|
||||||
|
* Datepicker for Bootstrap
|
||||||
|
*
|
||||||
|
* Copyright 2012 Stefan Petre
|
||||||
|
* Improvements by Andrew Rowls
|
||||||
|
* Licensed under the Apache License v2.0
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.datepicker {
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
direction: ltr;
|
||||||
|
/*.dow {
|
||||||
|
border-top: 1px solid #ddd !important;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
.datepicker-inline {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
.datepicker.datepicker-rtl {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.datepicker.datepicker-rtl table tr td span {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-bottom: 7px solid #ccc;
|
||||||
|
border-top: 0;
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid #fff;
|
||||||
|
border-top: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-left:before {
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-left:after {
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-right:before {
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-right:after {
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-top:before {
|
||||||
|
top: -7px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-top:after {
|
||||||
|
top: -6px;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-bottom:before {
|
||||||
|
bottom: -7px;
|
||||||
|
border-bottom: 0;
|
||||||
|
border-top: 7px solid #999;
|
||||||
|
}
|
||||||
|
.datepicker-dropdown.datepicker-orient-bottom:after {
|
||||||
|
bottom: -6px;
|
||||||
|
border-bottom: 0;
|
||||||
|
border-top: 6px solid #fff;
|
||||||
|
}
|
||||||
|
.datepicker > div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.datepicker.days div.datepicker-days {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.datepicker.months div.datepicker-months {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.datepicker.years div.datepicker-years {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.datepicker table {
|
||||||
|
margin: 0;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td,
|
||||||
|
.datepicker table tr th {
|
||||||
|
text-align: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.table-striped .datepicker table tr td,
|
||||||
|
.table-striped .datepicker table tr th {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.day:hover,
|
||||||
|
.datepicker table tr td.day.focused {
|
||||||
|
background: #eeeeee;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.old,
|
||||||
|
.datepicker table tr td.new {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.disabled,
|
||||||
|
.datepicker table tr td.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: #999999;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today,
|
||||||
|
.datepicker table tr td.today:hover,
|
||||||
|
.datepicker table tr td.today.disabled,
|
||||||
|
.datepicker table tr td.today.disabled:hover {
|
||||||
|
color: #000000;
|
||||||
|
background-color: #ffdb99;
|
||||||
|
border-color: #ffb733;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today:hover,
|
||||||
|
.datepicker table tr td.today:hover:hover,
|
||||||
|
.datepicker table tr td.today.disabled:hover,
|
||||||
|
.datepicker table tr td.today.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.today:focus,
|
||||||
|
.datepicker table tr td.today:hover:focus,
|
||||||
|
.datepicker table tr td.today.disabled:focus,
|
||||||
|
.datepicker table tr td.today.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.today:active,
|
||||||
|
.datepicker table tr td.today:hover:active,
|
||||||
|
.datepicker table tr td.today.disabled:active,
|
||||||
|
.datepicker table tr td.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.today.active,
|
||||||
|
.datepicker table tr td.today:hover.active,
|
||||||
|
.datepicker table tr td.today.disabled.active,
|
||||||
|
.datepicker table tr td.today.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
||||||
|
color: #000000;
|
||||||
|
background-color: #ffcd70;
|
||||||
|
border-color: #f59e00;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today:active,
|
||||||
|
.datepicker table tr td.today:hover:active,
|
||||||
|
.datepicker table tr td.today.disabled:active,
|
||||||
|
.datepicker table tr td.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.today.active,
|
||||||
|
.datepicker table tr td.today:hover.active,
|
||||||
|
.datepicker table tr td.today.disabled.active,
|
||||||
|
.datepicker table tr td.today.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today.disabled,
|
||||||
|
.datepicker table tr td.today:hover.disabled,
|
||||||
|
.datepicker table tr td.today.disabled.disabled,
|
||||||
|
.datepicker table tr td.today.disabled:hover.disabled,
|
||||||
|
.datepicker table tr td.today[disabled],
|
||||||
|
.datepicker table tr td.today:hover[disabled],
|
||||||
|
.datepicker table tr td.today.disabled[disabled],
|
||||||
|
.datepicker table tr td.today.disabled:hover[disabled],
|
||||||
|
fieldset[disabled] .datepicker table tr td.today,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
||||||
|
.datepicker table tr td.today.disabled:hover,
|
||||||
|
.datepicker table tr td.today:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.today.disabled.disabled:hover,
|
||||||
|
.datepicker table tr td.today.disabled:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.today[disabled]:hover,
|
||||||
|
.datepicker table tr td.today:hover[disabled]:hover,
|
||||||
|
.datepicker table tr td.today.disabled[disabled]:hover,
|
||||||
|
.datepicker table tr td.today.disabled:hover[disabled]:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.today.disabled:focus,
|
||||||
|
.datepicker table tr td.today:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.today.disabled.disabled:focus,
|
||||||
|
.datepicker table tr td.today.disabled:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.today[disabled]:focus,
|
||||||
|
.datepicker table tr td.today:hover[disabled]:focus,
|
||||||
|
.datepicker table tr td.today.disabled[disabled]:focus,
|
||||||
|
.datepicker table tr td.today.disabled:hover[disabled]:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.today.disabled:active,
|
||||||
|
.datepicker table tr td.today:hover.disabled:active,
|
||||||
|
.datepicker table tr td.today.disabled.disabled:active,
|
||||||
|
.datepicker table tr td.today.disabled:hover.disabled:active,
|
||||||
|
.datepicker table tr td.today[disabled]:active,
|
||||||
|
.datepicker table tr td.today:hover[disabled]:active,
|
||||||
|
.datepicker table tr td.today.disabled[disabled]:active,
|
||||||
|
.datepicker table tr td.today.disabled:hover[disabled]:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.today.disabled.active,
|
||||||
|
.datepicker table tr td.today:hover.disabled.active,
|
||||||
|
.datepicker table tr td.today.disabled.disabled.active,
|
||||||
|
.datepicker table tr td.today.disabled:hover.disabled.active,
|
||||||
|
.datepicker table tr td.today[disabled].active,
|
||||||
|
.datepicker table tr td.today:hover[disabled].active,
|
||||||
|
.datepicker table tr td.today.disabled[disabled].active,
|
||||||
|
.datepicker table tr td.today.disabled:hover[disabled].active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today:hover.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.today.disabled:hover.active {
|
||||||
|
background-color: #ffdb99;
|
||||||
|
border-color: #ffb733;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today:hover:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.today.active:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.range,
|
||||||
|
.datepicker table tr td.range:hover,
|
||||||
|
.datepicker table tr td.range.disabled,
|
||||||
|
.datepicker table tr td.range.disabled:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.range.today,
|
||||||
|
.datepicker table tr td.range.today:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover {
|
||||||
|
color: #000000;
|
||||||
|
background-color: #f7ca77;
|
||||||
|
border-color: #f1a417;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.range.today:hover,
|
||||||
|
.datepicker table tr td.range.today:hover:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.range.today:focus,
|
||||||
|
.datepicker table tr td.range.today:hover:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.range.today:active,
|
||||||
|
.datepicker table tr td.range.today:hover:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.range.today.active,
|
||||||
|
.datepicker table tr td.range.today:hover.active,
|
||||||
|
.datepicker table tr td.range.today.disabled.active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
||||||
|
color: #000000;
|
||||||
|
background-color: #f4bb51;
|
||||||
|
border-color: #bf800c;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.range.today:active,
|
||||||
|
.datepicker table tr td.range.today:hover:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.range.today.active,
|
||||||
|
.datepicker table tr td.range.today:hover.active,
|
||||||
|
.datepicker table tr td.range.today.disabled.active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.range.today.disabled,
|
||||||
|
.datepicker table tr td.range.today:hover.disabled,
|
||||||
|
.datepicker table tr td.range.today.disabled.disabled,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||||
|
.datepicker table tr td.range.today[disabled],
|
||||||
|
.datepicker table tr td.range.today:hover[disabled],
|
||||||
|
.datepicker table tr td.range.today.disabled[disabled],
|
||||||
|
.datepicker table tr td.range.today.disabled:hover[disabled],
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.range.today[disabled]:hover,
|
||||||
|
.datepicker table tr td.range.today:hover[disabled]:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled[disabled]:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover[disabled]:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.range.today.disabled:focus,
|
||||||
|
.datepicker table tr td.range.today:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled.disabled:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.range.today[disabled]:focus,
|
||||||
|
.datepicker table tr td.range.today:hover[disabled]:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled[disabled]:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover[disabled]:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.range.today.disabled:active,
|
||||||
|
.datepicker table tr td.range.today:hover.disabled:active,
|
||||||
|
.datepicker table tr td.range.today.disabled.disabled:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.disabled:active,
|
||||||
|
.datepicker table tr td.range.today[disabled]:active,
|
||||||
|
.datepicker table tr td.range.today:hover[disabled]:active,
|
||||||
|
.datepicker table tr td.range.today.disabled[disabled]:active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover[disabled]:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:active,
|
||||||
|
.datepicker table tr td.range.today.disabled.active,
|
||||||
|
.datepicker table tr td.range.today:hover.disabled.active,
|
||||||
|
.datepicker table tr td.range.today.disabled.disabled.active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover.disabled.active,
|
||||||
|
.datepicker table tr td.range.today[disabled].active,
|
||||||
|
.datepicker table tr td.range.today:hover[disabled].active,
|
||||||
|
.datepicker table tr td.range.today.disabled[disabled].active,
|
||||||
|
.datepicker table tr td.range.today.disabled:hover[disabled].active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today:hover.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover.active {
|
||||||
|
background-color: #f7ca77;
|
||||||
|
border-color: #f1a417;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.selected,
|
||||||
|
.datepicker table tr td.selected:hover,
|
||||||
|
.datepicker table tr td.selected.disabled,
|
||||||
|
.datepicker table tr td.selected.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #999999;
|
||||||
|
border-color: #555555;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.datepicker table tr td.selected:hover,
|
||||||
|
.datepicker table tr td.selected:hover:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.selected:focus,
|
||||||
|
.datepicker table tr td.selected:hover:focus,
|
||||||
|
.datepicker table tr td.selected.disabled:focus,
|
||||||
|
.datepicker table tr td.selected.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.selected:active,
|
||||||
|
.datepicker table tr td.selected:hover:active,
|
||||||
|
.datepicker table tr td.selected.disabled:active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover:active,
|
||||||
|
.datepicker table tr td.selected.active,
|
||||||
|
.datepicker table tr td.selected:hover.active,
|
||||||
|
.datepicker table tr td.selected.disabled.active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #858585;
|
||||||
|
border-color: #373737;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.selected:active,
|
||||||
|
.datepicker table tr td.selected:hover:active,
|
||||||
|
.datepicker table tr td.selected.disabled:active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover:active,
|
||||||
|
.datepicker table tr td.selected.active,
|
||||||
|
.datepicker table tr td.selected:hover.active,
|
||||||
|
.datepicker table tr td.selected.disabled.active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.selected.disabled,
|
||||||
|
.datepicker table tr td.selected:hover.disabled,
|
||||||
|
.datepicker table tr td.selected.disabled.disabled,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||||
|
.datepicker table tr td.selected[disabled],
|
||||||
|
.datepicker table tr td.selected:hover[disabled],
|
||||||
|
.datepicker table tr td.selected.disabled[disabled],
|
||||||
|
.datepicker table tr td.selected.disabled:hover[disabled],
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:hover,
|
||||||
|
.datepicker table tr td.selected:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.selected.disabled.disabled:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.selected[disabled]:hover,
|
||||||
|
.datepicker table tr td.selected:hover[disabled]:hover,
|
||||||
|
.datepicker table tr td.selected.disabled[disabled]:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:hover[disabled]:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.selected.disabled:focus,
|
||||||
|
.datepicker table tr td.selected:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.selected.disabled.disabled:focus,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.selected[disabled]:focus,
|
||||||
|
.datepicker table tr td.selected:hover[disabled]:focus,
|
||||||
|
.datepicker table tr td.selected.disabled[disabled]:focus,
|
||||||
|
.datepicker table tr td.selected.disabled:hover[disabled]:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.selected.disabled:active,
|
||||||
|
.datepicker table tr td.selected:hover.disabled:active,
|
||||||
|
.datepicker table tr td.selected.disabled.disabled:active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.disabled:active,
|
||||||
|
.datepicker table tr td.selected[disabled]:active,
|
||||||
|
.datepicker table tr td.selected:hover[disabled]:active,
|
||||||
|
.datepicker table tr td.selected.disabled[disabled]:active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover[disabled]:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:active,
|
||||||
|
.datepicker table tr td.selected.disabled.active,
|
||||||
|
.datepicker table tr td.selected:hover.disabled.active,
|
||||||
|
.datepicker table tr td.selected.disabled.disabled.active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover.disabled.active,
|
||||||
|
.datepicker table tr td.selected[disabled].active,
|
||||||
|
.datepicker table tr td.selected:hover[disabled].active,
|
||||||
|
.datepicker table tr td.selected.disabled[disabled].active,
|
||||||
|
.datepicker table tr td.selected.disabled:hover[disabled].active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected:hover.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.selected.disabled:hover.active {
|
||||||
|
background-color: #999999;
|
||||||
|
border-color: #555555;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.active,
|
||||||
|
.datepicker table tr td.active:hover,
|
||||||
|
.datepicker table tr td.active.disabled,
|
||||||
|
.datepicker table tr td.active.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #428bca;
|
||||||
|
border-color: #357ebd;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.datepicker table tr td.active:hover,
|
||||||
|
.datepicker table tr td.active:hover:hover,
|
||||||
|
.datepicker table tr td.active.disabled:hover,
|
||||||
|
.datepicker table tr td.active.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.active:focus,
|
||||||
|
.datepicker table tr td.active:hover:focus,
|
||||||
|
.datepicker table tr td.active.disabled:focus,
|
||||||
|
.datepicker table tr td.active.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.active:active,
|
||||||
|
.datepicker table tr td.active:hover:active,
|
||||||
|
.datepicker table tr td.active.disabled:active,
|
||||||
|
.datepicker table tr td.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td.active.active,
|
||||||
|
.datepicker table tr td.active:hover.active,
|
||||||
|
.datepicker table tr td.active.disabled.active,
|
||||||
|
.datepicker table tr td.active.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #3276b1;
|
||||||
|
border-color: #285e8e;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.active:active,
|
||||||
|
.datepicker table tr td.active:hover:active,
|
||||||
|
.datepicker table tr td.active.disabled:active,
|
||||||
|
.datepicker table tr td.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td.active.active,
|
||||||
|
.datepicker table tr td.active:hover.active,
|
||||||
|
.datepicker table tr td.active.disabled.active,
|
||||||
|
.datepicker table tr td.active.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td.active.disabled,
|
||||||
|
.datepicker table tr td.active:hover.disabled,
|
||||||
|
.datepicker table tr td.active.disabled.disabled,
|
||||||
|
.datepicker table tr td.active.disabled:hover.disabled,
|
||||||
|
.datepicker table tr td.active[disabled],
|
||||||
|
.datepicker table tr td.active:hover[disabled],
|
||||||
|
.datepicker table tr td.active.disabled[disabled],
|
||||||
|
.datepicker table tr td.active.disabled:hover[disabled],
|
||||||
|
fieldset[disabled] .datepicker table tr td.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
||||||
|
.datepicker table tr td.active.disabled:hover,
|
||||||
|
.datepicker table tr td.active:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.active.disabled.disabled:hover,
|
||||||
|
.datepicker table tr td.active.disabled:hover.disabled:hover,
|
||||||
|
.datepicker table tr td.active[disabled]:hover,
|
||||||
|
.datepicker table tr td.active:hover[disabled]:hover,
|
||||||
|
.datepicker table tr td.active.disabled[disabled]:hover,
|
||||||
|
.datepicker table tr td.active.disabled:hover[disabled]:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover:hover,
|
||||||
|
.datepicker table tr td.active.disabled:focus,
|
||||||
|
.datepicker table tr td.active:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.active.disabled.disabled:focus,
|
||||||
|
.datepicker table tr td.active.disabled:hover.disabled:focus,
|
||||||
|
.datepicker table tr td.active[disabled]:focus,
|
||||||
|
.datepicker table tr td.active:hover[disabled]:focus,
|
||||||
|
.datepicker table tr td.active.disabled[disabled]:focus,
|
||||||
|
.datepicker table tr td.active.disabled:hover[disabled]:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover:focus,
|
||||||
|
.datepicker table tr td.active.disabled:active,
|
||||||
|
.datepicker table tr td.active:hover.disabled:active,
|
||||||
|
.datepicker table tr td.active.disabled.disabled:active,
|
||||||
|
.datepicker table tr td.active.disabled:hover.disabled:active,
|
||||||
|
.datepicker table tr td.active[disabled]:active,
|
||||||
|
.datepicker table tr td.active:hover[disabled]:active,
|
||||||
|
.datepicker table tr td.active.disabled[disabled]:active,
|
||||||
|
.datepicker table tr td.active.disabled:hover[disabled]:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td.active.disabled.active,
|
||||||
|
.datepicker table tr td.active:hover.disabled.active,
|
||||||
|
.datepicker table tr td.active.disabled.disabled.active,
|
||||||
|
.datepicker table tr td.active.disabled:hover.disabled.active,
|
||||||
|
.datepicker table tr td.active[disabled].active,
|
||||||
|
.datepicker table tr td.active:hover[disabled].active,
|
||||||
|
.datepicker table tr td.active.disabled[disabled].active,
|
||||||
|
.datepicker table tr td.active.disabled:hover[disabled].active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active:hover.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td.active.disabled:hover.active {
|
||||||
|
background-color: #428bca;
|
||||||
|
border-color: #357ebd;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span {
|
||||||
|
display: block;
|
||||||
|
width: 23%;
|
||||||
|
height: 54px;
|
||||||
|
line-height: 54px;
|
||||||
|
float: left;
|
||||||
|
margin: 1%;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.disabled,
|
||||||
|
.datepicker table tr td span.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: #999999;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.active,
|
||||||
|
.datepicker table tr td span.active:hover,
|
||||||
|
.datepicker table tr td span.active.disabled,
|
||||||
|
.datepicker table tr td span.active.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #428bca;
|
||||||
|
border-color: #357ebd;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.active:hover,
|
||||||
|
.datepicker table tr td span.active:hover:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:hover:hover,
|
||||||
|
.datepicker table tr td span.active:focus,
|
||||||
|
.datepicker table tr td span.active:hover:focus,
|
||||||
|
.datepicker table tr td span.active.disabled:focus,
|
||||||
|
.datepicker table tr td span.active.disabled:hover:focus,
|
||||||
|
.datepicker table tr td span.active:active,
|
||||||
|
.datepicker table tr td span.active:hover:active,
|
||||||
|
.datepicker table tr td span.active.disabled:active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td span.active.active,
|
||||||
|
.datepicker table tr td span.active:hover.active,
|
||||||
|
.datepicker table tr td span.active.disabled.active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #3276b1;
|
||||||
|
border-color: #285e8e;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.active:active,
|
||||||
|
.datepicker table tr td span.active:hover:active,
|
||||||
|
.datepicker table tr td span.active.disabled:active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td span.active.active,
|
||||||
|
.datepicker table tr td span.active:hover.active,
|
||||||
|
.datepicker table tr td span.active.disabled.active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
||||||
|
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.active.disabled,
|
||||||
|
.datepicker table tr td span.active:hover.disabled,
|
||||||
|
.datepicker table tr td span.active.disabled.disabled,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||||
|
.datepicker table tr td span.active[disabled],
|
||||||
|
.datepicker table tr td span.active:hover[disabled],
|
||||||
|
.datepicker table tr td span.active.disabled[disabled],
|
||||||
|
.datepicker table tr td span.active.disabled:hover[disabled],
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:hover,
|
||||||
|
.datepicker table tr td span.active:hover.disabled:hover,
|
||||||
|
.datepicker table tr td span.active.disabled.disabled:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.disabled:hover,
|
||||||
|
.datepicker table tr td span.active[disabled]:hover,
|
||||||
|
.datepicker table tr td span.active:hover[disabled]:hover,
|
||||||
|
.datepicker table tr td span.active.disabled[disabled]:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
|
||||||
|
.datepicker table tr td span.active.disabled:focus,
|
||||||
|
.datepicker table tr td span.active:hover.disabled:focus,
|
||||||
|
.datepicker table tr td span.active.disabled.disabled:focus,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.disabled:focus,
|
||||||
|
.datepicker table tr td span.active[disabled]:focus,
|
||||||
|
.datepicker table tr td span.active:hover[disabled]:focus,
|
||||||
|
.datepicker table tr td span.active.disabled[disabled]:focus,
|
||||||
|
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
|
||||||
|
.datepicker table tr td span.active.disabled:active,
|
||||||
|
.datepicker table tr td span.active:hover.disabled:active,
|
||||||
|
.datepicker table tr td span.active.disabled.disabled:active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.disabled:active,
|
||||||
|
.datepicker table tr td span.active[disabled]:active,
|
||||||
|
.datepicker table tr td span.active:hover[disabled]:active,
|
||||||
|
.datepicker table tr td span.active.disabled[disabled]:active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover[disabled]:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:active,
|
||||||
|
.datepicker table tr td span.active.disabled.active,
|
||||||
|
.datepicker table tr td span.active:hover.disabled.active,
|
||||||
|
.datepicker table tr td span.active.disabled.disabled.active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover.disabled.active,
|
||||||
|
.datepicker table tr td span.active[disabled].active,
|
||||||
|
.datepicker table tr td span.active:hover[disabled].active,
|
||||||
|
.datepicker table tr td span.active.disabled[disabled].active,
|
||||||
|
.datepicker table tr td span.active.disabled:hover[disabled].active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active:hover.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled.active,
|
||||||
|
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.active {
|
||||||
|
background-color: #428bca;
|
||||||
|
border-color: #357ebd;
|
||||||
|
}
|
||||||
|
.datepicker table tr td span.old,
|
||||||
|
.datepicker table tr td span.new {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.datepicker th.datepicker-switch {
|
||||||
|
width: 145px;
|
||||||
|
}
|
||||||
|
.datepicker thead tr:first-child th,
|
||||||
|
.datepicker tfoot tr th {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.datepicker thead tr:first-child th:hover,
|
||||||
|
.datepicker tfoot tr th:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
.datepicker .cw {
|
||||||
|
font-size: 10px;
|
||||||
|
width: 12px;
|
||||||
|
padding: 0 2px 0 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.datepicker thead tr:first-child th.cw {
|
||||||
|
cursor: default;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.input-group.date .input-group-addon i {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
.input-daterange input {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.input-daterange input:first-child {
|
||||||
|
border-radius: 3px 0 0 3px;
|
||||||
|
}
|
||||||
|
.input-daterange input:last-child {
|
||||||
|
border-radius: 0 3px 3px 0;
|
||||||
|
}
|
||||||
|
.input-daterange .input-group-addon {
|
||||||
|
width: auto;
|
||||||
|
min-width: 16px;
|
||||||
|
padding: 4px 5px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.428571429;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 1px 0 #fff;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
border-width: 1px 0;
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
.datepicker.dropdown-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
float: left;
|
||||||
|
display: none;
|
||||||
|
min-width: 160px;
|
||||||
|
list-style: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
-webkit-background-clip: padding-box;
|
||||||
|
-moz-background-clip: padding;
|
||||||
|
background-clip: padding-box;
|
||||||
|
*border-right-width: 2px;
|
||||||
|
*border-bottom-width: 2px;
|
||||||
|
color: #333333;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.428571429;
|
||||||
|
}
|
||||||
|
.datepicker.dropdown-menu th,
|
||||||
|
.datepicker.dropdown-menu td {
|
||||||
|
padding: 4px 5px;
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
/*
|
||||||
|
Common
|
||||||
|
*/
|
||||||
|
|
||||||
|
.wizard,
|
||||||
|
.tabcontrol
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard a,
|
||||||
|
.tabcontrol a
|
||||||
|
{
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard ul,
|
||||||
|
.tabcontrol ul
|
||||||
|
{
|
||||||
|
list-style: none !important;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard ul > li,
|
||||||
|
.tabcontrol ul > li
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessibility */
|
||||||
|
.wizard > .steps .current-info,
|
||||||
|
.tabcontrol > .steps .current-info
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
left: -999em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .title,
|
||||||
|
.tabcontrol > .content > .title
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
left: -999em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wizard
|
||||||
|
*/
|
||||||
|
|
||||||
|
.wizard > .steps
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard.vertical > .steps
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
float: left;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps > ul > li
|
||||||
|
{
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps > ul > li,
|
||||||
|
.wizard > .actions > ul > li
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard.vertical > .steps > ul > li
|
||||||
|
{
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps a,
|
||||||
|
.wizard > .steps a:hover,
|
||||||
|
.wizard > .steps a:active
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
margin: 0 0.5em 0.5em;
|
||||||
|
padding: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps .disabled a,
|
||||||
|
.wizard > .steps .disabled a:hover,
|
||||||
|
.wizard > .steps .disabled a:active
|
||||||
|
{
|
||||||
|
background: #eee;
|
||||||
|
color: #aaa;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps .current a,
|
||||||
|
.wizard > .steps .current a:hover,
|
||||||
|
.wizard > .steps .current a:active
|
||||||
|
{
|
||||||
|
background: #1AB394;
|
||||||
|
color: #fff;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps .done a,
|
||||||
|
.wizard > .steps .done a:hover,
|
||||||
|
.wizard > .steps .done a:active
|
||||||
|
{
|
||||||
|
background: #6fd1bd;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .steps .error a,
|
||||||
|
.wizard > .steps .error a:hover,
|
||||||
|
.wizard > .steps .error a:active
|
||||||
|
{
|
||||||
|
background: #ED5565 ;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content
|
||||||
|
{
|
||||||
|
background: #eee;
|
||||||
|
display: block;
|
||||||
|
margin: 5px 5px 10px 5px;
|
||||||
|
min-height: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-big.wizard > .content {
|
||||||
|
min-height: 320px;
|
||||||
|
}
|
||||||
|
.wizard.vertical > .content
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
float: left;
|
||||||
|
margin: 0 2.5% 0.5em 2.5%;
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
position: absolute;
|
||||||
|
width: 95%;
|
||||||
|
height: 95%;
|
||||||
|
padding: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body ul
|
||||||
|
{
|
||||||
|
list-style: disc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body ul > li
|
||||||
|
{
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body > iframe
|
||||||
|
{
|
||||||
|
border: 0 none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body input
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body input[type="checkbox"]
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body input.error
|
||||||
|
{
|
||||||
|
background: rgb(251, 227, 228);
|
||||||
|
border: 1px solid #fbc2c4;
|
||||||
|
color: #8a1f11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body label
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .content > .body label.error
|
||||||
|
{
|
||||||
|
color: #8a1f11;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .actions
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard.vertical > .actions
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
float: right;
|
||||||
|
margin: 0 2.5%;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .actions > ul
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .actions > ul > li
|
||||||
|
{
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard.vertical > .actions > ul > li
|
||||||
|
{
|
||||||
|
margin: 0 0 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .actions a,
|
||||||
|
.wizard > .actions a:hover,
|
||||||
|
.wizard > .actions a:active
|
||||||
|
{
|
||||||
|
background: #1AB394;
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .actions .disabled a,
|
||||||
|
.wizard > .actions .disabled a:hover,
|
||||||
|
.wizard > .actions .disabled a:active
|
||||||
|
{
|
||||||
|
background: #eee;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .loading
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard > .loading .spinner
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tabcontrol
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tabcontrol > .steps
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
top: 1px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
margin: 5px 2px 0 0;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
-webkit-border-top-left-radius: 5px;
|
||||||
|
-webkit-border-top-right-radius: 5px;
|
||||||
|
-moz-border-radius-topleft: 5px;
|
||||||
|
-moz-border-radius-topright: 5px;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li:hover
|
||||||
|
{
|
||||||
|
background: #edecec;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li.current
|
||||||
|
{
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-bottom: 0 none;
|
||||||
|
padding: 0 0 1px 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li > a
|
||||||
|
{
|
||||||
|
color: #5f5f5f;
|
||||||
|
display: inline-block;
|
||||||
|
border: 0 none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 30px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li > a:hover
|
||||||
|
{
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .steps > ul > li.current > a
|
||||||
|
{
|
||||||
|
padding: 15px 30px 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .content
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
height: 35em;
|
||||||
|
overflow: hidden;
|
||||||
|
border-top: 1px solid #bbb;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .content > .body
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
position: absolute;
|
||||||
|
width: 95%;
|
||||||
|
height: 95%;
|
||||||
|
padding: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .content > .body ul
|
||||||
|
{
|
||||||
|
list-style: disc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontrol > .content > .body ul > li
|
||||||
|
{
|
||||||
|
display: list-item;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
define("echarts/chart/heatmap",["require","./base","../layer/heatmap","../config","../util/ecData","zrender/tool/util","zrender/tool/color","zrender/shape/Image","../chart"],function(e){function t(e,t,n,a,o){i.call(this,e,t,n,a,o),this.refresh(a)}var i=e("./base"),n=e("../layer/heatmap"),a=e("../config"),o=(e("../util/ecData"),e("zrender/tool/util")),r=(e("zrender/tool/color"),e("zrender/shape/Image"));return a.heatmap={zlevel:0,z:2,clickable:!0},t.prototype={type:a.CHART_TYPE_HEATMAP,refresh:function(e){this.clear(),e&&(this.option=e,this.series=e.series),this._init()},_init:function(){var e=this.series;this.backupShapeList();for(var t=e.length,i=0;t>i;++i)if(e[i].type===a.CHART_TYPE_HEATMAP){e[i]=this.reformOption(e[i]);var o=new n(e[i]),s=o.getCanvas(e[i].data,this.zr.getWidth(),this.zr.getHeight()),l=new r({position:[0,0],scale:[1,1],hoverable:this.option.hoverable,style:{x:0,y:0,image:s,width:s.width,height:s.height}});this.shapeList.push(l)}this.addShapeList()}},o.inherits(t,i),e("../chart").define("heatmap",t),t}),define("echarts/layer/heatmap",["require"],function(){function e(e){if(this.option=e,e)for(var i in t)this.option[i]=void 0!==e[i]?e[i]:t[i];else this.option=t}var t={blurSize:30,gradientColors:["blue","cyan","lime","yellow","red"],minAlpha:.05,valueScale:1,opacity:1},i=20,n=256;return e.prototype={getCanvas:function(e,t,a){var o=this._getBrush(),r=this._getGradient(),s=i+this.option.blurSize,l=document.createElement("canvas");l.width=t,l.height=a;for(var h=l.getContext("2d"),m=e.length,V=0;m>V;++V){var d=e[V],U=d[0],p=d[1],c=d[2],u=Math.min(1,Math.max(c*this.option.valueScale||this.option.minAlpha,this.option.minAlpha));h.globalAlpha=u,h.drawImage(o,U-s,p-s)}for(var g=h.getImageData(0,0,l.width,l.height),y=g.data,m=y.length/4;m--;){var b=4*m+3,u=y[b]/256,f=Math.floor(u*(n-1));y[b-3]=r[4*f],y[b-2]=r[4*f+1],y[b-1]=r[4*f+2],y[b]*=this.option.opacity}return h.putImageData(g,0,0),l},_getBrush:function(){if(!this._brushCanvas){this._brushCanvas=document.createElement("canvas");var e=i+this.option.blurSize,t=2*e;this._brushCanvas.width=t,this._brushCanvas.height=t;var n=this._brushCanvas.getContext("2d");n.shadowOffsetX=t,n.shadowBlur=this.option.blurSize,n.shadowColor="black",n.beginPath(),n.arc(-e,e,i,0,2*Math.PI,!0),n.closePath(),n.fill()}return this._brushCanvas},_getGradient:function(){if(!this._gradientPixels){var e=n,t=document.createElement("canvas");t.width=1,t.height=e;for(var i=t.getContext("2d"),a=i.createLinearGradient(0,0,0,e),o=this.option.gradientColors.length,r=0;o>r;++r)"string"==typeof this.option.gradientColors[r]?a.addColorStop((r+1)/o,this.option.gradientColors[r]):a.addColorStop(this.option.gradientColors[r].offset,this.option.gradientColors[r].color);i.fillStyle=a,i.fillRect(0,0,1,e),this._gradientPixels=i.getImageData(0,0,1,e).data}return this._gradientPixels}},e}),define("echarts/layer/heatmap",["require"],function(){function e(e){if(this.option=e,e)for(var i in t)this.option[i]=void 0!==e[i]?e[i]:t[i];else this.option=t}var t={blurSize:30,gradientColors:["blue","cyan","lime","yellow","red"],minAlpha:.05,valueScale:1,opacity:1},i=20,n=256;return e.prototype={getCanvas:function(e,t,a){var o=this._getBrush(),r=this._getGradient(),s=i+this.option.blurSize,l=document.createElement("canvas");l.width=t,l.height=a;for(var h=l.getContext("2d"),m=e.length,V=0;m>V;++V){var d=e[V],U=d[0],p=d[1],c=d[2],u=Math.min(1,Math.max(c*this.option.valueScale||this.option.minAlpha,this.option.minAlpha));h.globalAlpha=u,h.drawImage(o,U-s,p-s)}for(var g=h.getImageData(0,0,l.width,l.height),y=g.data,m=y.length/4;m--;){var b=4*m+3,u=y[b]/256,f=Math.floor(u*(n-1));y[b-3]=r[4*f],y[b-2]=r[4*f+1],y[b-1]=r[4*f+2],y[b]*=this.option.opacity}return h.putImageData(g,0,0),l},_getBrush:function(){if(!this._brushCanvas){this._brushCanvas=document.createElement("canvas");var e=i+this.option.blurSize,t=2*e;this._brushCanvas.width=t,this._brushCanvas.height=t;var n=this._brushCanvas.getContext("2d");n.shadowOffsetX=t,n.shadowBlur=this.option.blurSize,n.shadowColor="black",n.beginPath(),n.arc(-e,e,i,0,2*Math.PI,!0),n.closePath(),n.fill()}return this._brushCanvas},_getGradient:function(){if(!this._gradientPixels){var e=n,t=document.createElement("canvas");t.width=1,t.height=e;for(var i=t.getContext("2d"),a=i.createLinearGradient(0,0,0,e),o=this.option.gradientColors.length,r=0;o>r;++r)"string"==typeof this.option.gradientColors[r]?a.addColorStop((r+1)/o,this.option.gradientColors[r]):a.addColorStop(this.option.gradientColors[r].offset,this.option.gradientColors[r].color);i.fillStyle=a,i.fillRect(0,0,1,e),this._gradientPixels=i.getImageData(0,0,1,e).data}return this._gradientPixels}},e});
|
|
@ -0,0 +1,196 @@
|
||||||
|
/* ShiftCheckbox jQuery plugin
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2012 James Nylen
|
||||||
|
*
|
||||||
|
* Released under MIT license
|
||||||
|
* For details see:
|
||||||
|
* https://github.com/nylen/shiftcheckbox
|
||||||
|
*
|
||||||
|
* Requires jQuery v1.7 or higher.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
var ns = '.shiftcheckbox';
|
||||||
|
|
||||||
|
$.fn.shiftcheckbox = function(opts) {
|
||||||
|
opts = $.extend({
|
||||||
|
checkboxSelector : null,
|
||||||
|
selectAll : null,
|
||||||
|
onChange : null,
|
||||||
|
ignoreClick : null
|
||||||
|
}, opts);
|
||||||
|
|
||||||
|
if (typeof opts.onChange != 'function') {
|
||||||
|
opts.onChange = function(checked) { };
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.scb_changeChecked = function(opts, checked) {
|
||||||
|
this.prop('checked', checked);
|
||||||
|
opts.onChange.call(this, checked);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $containers,
|
||||||
|
$checkboxes,
|
||||||
|
$containersSelectAll,
|
||||||
|
$checkboxesSelectAll,
|
||||||
|
$otherSelectAll,
|
||||||
|
$containersAll,
|
||||||
|
$checkboxesAll;
|
||||||
|
|
||||||
|
if (opts.selectAll) {
|
||||||
|
// We need to set up a "select all" control
|
||||||
|
$containersSelectAll = $(opts.selectAll);
|
||||||
|
if ($containersSelectAll && !$containersSelectAll.length) {
|
||||||
|
$containersSelectAll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($containersSelectAll) {
|
||||||
|
$checkboxesSelectAll = $containersSelectAll
|
||||||
|
.filter(':checkbox')
|
||||||
|
.add($containersSelectAll.find(':checkbox'));
|
||||||
|
|
||||||
|
$containersSelectAll = $containersSelectAll.not(':checkbox');
|
||||||
|
$otherSelectAll = $containersSelectAll.filter(function() {
|
||||||
|
return !$(this).find($checkboxesSelectAll).length;
|
||||||
|
});
|
||||||
|
$containersSelectAll = $containersSelectAll.filter(function() {
|
||||||
|
return !!$(this).find($checkboxesSelectAll).length;
|
||||||
|
}).each(function() {
|
||||||
|
$(this).data('childCheckbox', $(this).find($checkboxesSelectAll)[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.checkboxSelector) {
|
||||||
|
|
||||||
|
// checkboxSelector means that the elements we need to attach handlers to
|
||||||
|
// ($containers) are not actually checkboxes but contain them instead
|
||||||
|
|
||||||
|
$containersAll = this.filter(function() {
|
||||||
|
return !!$(this).find(opts.checkboxSelector).filter(':checkbox').length;
|
||||||
|
}).each(function() {
|
||||||
|
$(this).data('childCheckbox', $(this).find(opts.checkboxSelector).filter(':checkbox')[0]);
|
||||||
|
}).add($containersSelectAll);
|
||||||
|
|
||||||
|
$checkboxesAll = $containersAll.map(function() {
|
||||||
|
return $(this).data('childCheckbox');
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$checkboxesAll = this.filter(':checkbox');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkboxesSelectAll && !$checkboxesSelectAll.length) {
|
||||||
|
$checkboxesSelectAll = false;
|
||||||
|
} else {
|
||||||
|
$checkboxesAll = $checkboxesAll.add($checkboxesSelectAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($otherSelectAll && !$otherSelectAll.length) {
|
||||||
|
$otherSelectAll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($containersAll) {
|
||||||
|
$containers = $containersAll.not($containersSelectAll);
|
||||||
|
}
|
||||||
|
$checkboxes = $checkboxesAll.not($checkboxesSelectAll);
|
||||||
|
|
||||||
|
if (!$checkboxes.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastIndex = -1;
|
||||||
|
|
||||||
|
var checkboxClicked = function(e) {
|
||||||
|
var checked = !!$(this).prop('checked');
|
||||||
|
|
||||||
|
var curIndex = $checkboxes.index(this);
|
||||||
|
if (curIndex < 0) {
|
||||||
|
if ($checkboxesSelectAll.filter(this).length) {
|
||||||
|
$checkboxesAll.scb_changeChecked(opts, checked);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.shiftKey && lastIndex != -1) {
|
||||||
|
var di = (curIndex > lastIndex ? 1 : -1);
|
||||||
|
for (var i = lastIndex; i != curIndex; i += di) {
|
||||||
|
$checkboxes.eq(i).scb_changeChecked(opts, checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkboxesSelectAll) {
|
||||||
|
if (checked && !$checkboxes.not(':checked').length) {
|
||||||
|
$checkboxesSelectAll.scb_changeChecked(opts, true);
|
||||||
|
} else if (!checked) {
|
||||||
|
$checkboxesSelectAll.scb_changeChecked(opts, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = curIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($checkboxesSelectAll) {
|
||||||
|
$checkboxesSelectAll
|
||||||
|
.prop('checked', !$checkboxes.not(':checked').length)
|
||||||
|
.filter(function() {
|
||||||
|
return !$containersAll.find(this).length;
|
||||||
|
}).on('click' + ns, checkboxClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($otherSelectAll) {
|
||||||
|
$otherSelectAll.on('click' + ns, function() {
|
||||||
|
var checked;
|
||||||
|
if ($checkboxesSelectAll) {
|
||||||
|
checked = !!$checkboxesSelectAll.eq(0).prop('checked');
|
||||||
|
} else {
|
||||||
|
checked = !!$checkboxes.eq(0).prop('checked');
|
||||||
|
}
|
||||||
|
$checkboxesAll.scb_changeChecked(opts, !checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.checkboxSelector) {
|
||||||
|
$containersAll.on('click' + ns, function(e) {
|
||||||
|
if ($(e.target).closest(opts.ignoreClick).length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $checkbox = $($(this).data('childCheckbox'));
|
||||||
|
$checkbox.not(e.target).each(function() {
|
||||||
|
var checked = !$checkbox.prop('checked');
|
||||||
|
$(this).scb_changeChecked(opts, checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
$checkbox[0].focus();
|
||||||
|
checkboxClicked.call($checkbox, e);
|
||||||
|
|
||||||
|
// If the user clicked on a label inside the row that points to the
|
||||||
|
// current row's checkbox, cancel the event.
|
||||||
|
var $label = $(e.target).closest('label');
|
||||||
|
var labelFor = $label.attr('for');
|
||||||
|
if (labelFor && labelFor == $checkbox.attr('id')) {
|
||||||
|
if ($label.find($checkbox).length) {
|
||||||
|
// Special case: The label contains the checkbox.
|
||||||
|
if ($checkbox[0] != e.target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).on('mousedown' + ns, function(e) {
|
||||||
|
if (e.shiftKey) {
|
||||||
|
// Prevent selecting text by Shift+click
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$checkboxes.on('click' + ns, checkboxClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
})(jQuery);
|
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 701 B |
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
WSSH Javascript Client
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
var client = new WSSHClient();
|
||||||
|
|
||||||
|
client.connect({
|
||||||
|
// Connection and authentication parameters
|
||||||
|
username: 'root',
|
||||||
|
hostname: 'localhost',
|
||||||
|
authentication_method: 'password', // can either be password or private_key
|
||||||
|
password: 'secretpassword', // do not provide when using private_key
|
||||||
|
key_passphrase: 'secretpassphrase', // *may* be provided if the private_key is encrypted
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
onError: function(error) {
|
||||||
|
// Called upon an error
|
||||||
|
console.error(error);
|
||||||
|
},
|
||||||
|
onConnect: function() {
|
||||||
|
// Called after a successful connection to the server
|
||||||
|
console.debug('Connected!');
|
||||||
|
|
||||||
|
client.send('ls\n'); // You can send data back to the server by using WSSHClient.send()
|
||||||
|
},
|
||||||
|
onClose: function() {
|
||||||
|
// Called when the remote closes the connection
|
||||||
|
console.debug('Connection Reset By Peer');
|
||||||
|
},
|
||||||
|
onData: function(data) {
|
||||||
|
// Called when data is received from the server
|
||||||
|
console.debug('Received: ' + data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function WSSHClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WSSHClient.prototype._generateEndpoint = function(options) {
|
||||||
|
console.log(options);
|
||||||
|
if (window.location.protocol == 'https:') {
|
||||||
|
var protocol = 'wss://';
|
||||||
|
} else {
|
||||||
|
var protocol = 'ws://';
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpoint = protocol + window.location.host + ':8080' + '/terminal';
|
||||||
|
return endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
WSSHClient.prototype.connect = function(options) {
|
||||||
|
var endpoint = this._generateEndpoint(options);
|
||||||
|
|
||||||
|
if (window.WebSocket) {
|
||||||
|
this._connection = new WebSocket(endpoint);
|
||||||
|
}
|
||||||
|
else if (window.MozWebSocket) {
|
||||||
|
this._connection = MozWebSocket(endpoint);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.onError('WebSocket Not Supported');
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._connection.onopen = function() {
|
||||||
|
options.onConnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
this._connection.onmessage = function (evt) {
|
||||||
|
var data = JSON.parse(evt.data.toString());
|
||||||
|
if (data.error !== undefined) {
|
||||||
|
options.onError(data.error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.onData(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._connection.onclose = function(evt) {
|
||||||
|
options.onClose();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
WSSHClient.prototype.send = function(data) {
|
||||||
|
this._connection.send(JSON.stringify({'data': data}));
|
||||||
|
};
|
|
@ -0,0 +1,199 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta name="viewport" content="width=320, initial-scale=1">
|
||||||
|
<title>Jumpserver Exec Terminal</title>
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="chat_box" class="content">
|
||||||
|
<p class="system"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<div class="content">
|
||||||
|
<input type="text" id="pattern" value="{{ check_assets }}" placeholder="Ansible Pattern">
|
||||||
|
<input type="text" id="command" placeholder="Command to execute" onkeypress="if(event.keyCode==13){sendMessage()}">
|
||||||
|
<input type="button" id="send_btn" value="Send" onclick="sendMessage()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var wsUri = "{{ web_terminal_uri }}"; //请求的websocket url
|
||||||
|
var ws = new WebSocket(wsUri);
|
||||||
|
|
||||||
|
function createSystemMessage(message) {
|
||||||
|
var message = document.createTextNode(message);
|
||||||
|
var messageBox = document.createElement('p');
|
||||||
|
messageBox.className = 'system';
|
||||||
|
messageBox.appendChild(message);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.appendChild(messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUserMessage(message) {
|
||||||
|
message = message.replace('/\n/g', '<br>');
|
||||||
|
var messageOb = document.createElement('div');
|
||||||
|
messageOb.innerHTML = message;
|
||||||
|
var messageBox = document.createElement('p');
|
||||||
|
messageBox.appendChild(messageOb);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.appendChild(messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onopen = function(ev) {
|
||||||
|
createSystemMessage('[Connected]');
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function(ev) {
|
||||||
|
createSystemMessage('[Disconnected]');
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
createUserMessage(ev.data);
|
||||||
|
var chat = document.getElementById('chat_box');
|
||||||
|
chat.scrollTop = chat.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendMessage() {
|
||||||
|
var pattern = document.getElementById('pattern');
|
||||||
|
var command = document.getElementById('command');
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
pattern: pattern.value,
|
||||||
|
command: command.value,
|
||||||
|
{# ts: (new Date()).getTime()#}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.send(JSON.stringify(data));
|
||||||
|
command.value = ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
{# background-color: #ecf0f1;#}
|
||||||
|
{# border: #000 solid 5px;#}
|
||||||
|
background: #000;
|
||||||
|
width: 720px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat_box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
height: 50px;
|
||||||
|
width: 720px;
|
||||||
|
{# border: #000 solid -10px;#}
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer .content {
|
||||||
|
padding-top: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pattern { width: 25%; }
|
||||||
|
#command { width: 60%; }
|
||||||
|
#send_btn {
|
||||||
|
width: 10%;
|
||||||
|
{# position: absolute;#}
|
||||||
|
margin-left: 5px;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 720px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="button"] {
|
||||||
|
border: 0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
background-color: #146EA8;
|
||||||
|
padding: 3px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="button"] {
|
||||||
|
background-color: #f39c12;
|
||||||
|
border-right: 2px solid #e67e22;
|
||||||
|
border-bottom: 2px solid #e67e22;
|
||||||
|
min-width: 70px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="button"]:hover {
|
||||||
|
background-color: #e67e22;
|
||||||
|
border-right: 2px solid #f39c12;
|
||||||
|
border-bottom: 2px solid #f39c12;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system,
|
||||||
|
.username {
|
||||||
|
color: #aaa;
|
||||||
|
font-style: italic;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 1000px) {
|
||||||
|
.content { width: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 780px) {
|
||||||
|
#footer { height: 51px; }
|
||||||
|
#chat_box { padding-bottom: 91px; }
|
||||||
|
|
||||||
|
#user { width: 100%; }
|
||||||
|
#message { width: 80%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 400px) {
|
||||||
|
#footer { height: 135px; }
|
||||||
|
#chat_box { padding-bottom: 135px; }
|
||||||
|
|
||||||
|
#message { width: 100%; }
|
||||||
|
#send_btn {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 3px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</html>
|
|
@ -0,0 +1,199 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 填写资产基本信息 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel blank-panel">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active"><a href="{% url 'asset_add' %}" class="text-center"><i class="fa fa-laptop"></i> 单台添加 </a></li>
|
||||||
|
<li><a href="{% url 'asset_add_batch' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 批量添加 </a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div id="tab-1" class="ibox float-e-margins tab-pane active">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form id="assetForm" method="post" class="form-horizontal">
|
||||||
|
|
||||||
|
{{ af.hostname|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.ip|bootstrap_horizontal }}
|
||||||
|
<p class="col-sm-offset-2">Tips: 如果IP地址不填写, IP默认会设置与主机名一致</p>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="j_group" class="col-sm-2 control-label">管理账号<span class="red-fonts"> *</span></label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" checked="checked" id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="col-sm-offset-2">Tips: 管理账号是服务器存在的root等高权限账号,用来推送新建系统用户</p>
|
||||||
|
<div class="form-group" id="admin_account" style="display: none">
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<label class="col-sm-2 control-label"> 管理用户名<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input type="text" placeholder="Username" name="username" class="form-control">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="col-sm-1 control-label"> 密码<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="password" placeholder="Password" name="password" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" id="id_port">
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" placeholder="Port" name="port" class="form-control" value="{{ default_port }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.group|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
{# {{ af.is_active|bootstrap_horizontal }}#}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label"> 是否激活<span class="red-fonts"> *</span> </label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label> <input type="radio" checked="" value="1" name="is_active">激活 </label>
|
||||||
|
<label> <input type="radio" value="0" name="is_active"> 禁用</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset"> 重置 </button>
|
||||||
|
<button class="btn btn-primary" type="submit"> 提交 </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('document').ready(function(){
|
||||||
|
var check_default = "{{ default_setting.name }}";
|
||||||
|
console.log(check_default);
|
||||||
|
if (check_default != 'default'){
|
||||||
|
$('#id_use_default_auth').attr('disabled', true);
|
||||||
|
$('#id_use_default_auth').attr('checked', false);
|
||||||
|
$('#admin_account').css('display', 'block');
|
||||||
|
} else {
|
||||||
|
$('#id_use_default_auth').click(function(){
|
||||||
|
if ($(this).is(':checked')){
|
||||||
|
$('#admin_account').css('display', 'none');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#admin_account').css('display', 'block');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var required_fields = ["id_hostname", "id_port"];
|
||||||
|
required_fields.forEach(function(field) {
|
||||||
|
$('label[for="' + field + '"]').parent().addClass("required");
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#assetForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_ip: [/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 'ip地址不正确'],
|
||||||
|
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||||
|
use_default_auth: function() {
|
||||||
|
var str1 = $("#id_use_default_auth").is(":checked");
|
||||||
|
if (str1 == true){
|
||||||
|
var decide = false;
|
||||||
|
} else {
|
||||||
|
var decide = true;
|
||||||
|
}
|
||||||
|
return decide}
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
"ip": {
|
||||||
|
rule: "check_ip;",
|
||||||
|
tip: "输入IP",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"hostname": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "填写主机名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "输入端口号",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
rule: "required(use_default_auth)",
|
||||||
|
tip: "输入用户名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
rule: "required(use_default_auth)",
|
||||||
|
tip: "输入密码",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,64 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<style>
|
||||||
|
.file-box{ position:relative;width:340px}
|
||||||
|
.txt{ height:22px; border:1px solid #cdcdcd; width:180px;}
|
||||||
|
.file{ position:absolute; top:0; right:80px; height:24px; filter:alpha(opacity:0);opacity: 0;width:260px }
|
||||||
|
</style>
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 填写主机基本信息 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel blank-panel">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li><a href="{% url 'asset_add' %}" class="text-center"><i class="fa fa-laptop"></i> 单台添加 </a></li>
|
||||||
|
<li class="active"><a href="{% url 'asset_add_batch' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 批量添加 </a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div id="tab-2" class="ibox float-e-margins tab-pane active">
|
||||||
|
{% if emg %}
|
||||||
|
<div class="alert alert-warning text-center">{{ emg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if smg %}
|
||||||
|
<div class="alert alert-success text-center">{{ smg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<p>请下载Excel文件, 按照格式填写主机信息, 上传导入. <a href="/static/files/excels/asset.xlsx">点击下载模板</a></p>
|
||||||
|
<form action="{% url 'asset_upload' %}" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="file-box">
|
||||||
|
<input id='textfield' />
|
||||||
|
<input type="button" class="btn btn-info btn-sm" name="file_name" value="点击选择文件">
|
||||||
|
<input type="file" name="file_name" class="file" id="fileField" size="28" onchange="document.getElementById('textfield').value=this.value" />
|
||||||
|
<button class="btn btn-primary btn-sm" type="submit">上传文件</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,354 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins" id="all">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 主机详细信息列表</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form id="asset_form">
|
||||||
|
<div class="col-sm-7" style="padding-left: 0px">
|
||||||
|
{# <label>#}
|
||||||
|
{# <select name="idc" class="form-control m-b input-sm" onchange="change_info()">#}
|
||||||
|
{# <option value="">机房</option>#}
|
||||||
|
{# {% for idc in idc_all %}#}
|
||||||
|
{# {% ifequal idc.name idc_name %}#}
|
||||||
|
{# <option value="{{idc.name}}" selected> {{ idc.name|slice:":20" }}</option>#}
|
||||||
|
{# {% else %}#}
|
||||||
|
{# <option value="{{idc.name}}"> {{ idc.name|slice:":20" }}</option>#}
|
||||||
|
{# {% endifequal %}#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
{# </select>#}
|
||||||
|
{# </label>#}
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="group" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">主机组</option>
|
||||||
|
{% for asset_group in asset_group_all %}
|
||||||
|
{% ifequal asset_group.name group_name %}
|
||||||
|
<option value="{{ asset_group.name }}" selected> {{ asset_group.name|slice:":20" }} </option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ asset_group.name }}"> {{ asset_group.name|slice:":20" }} </option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="asset_type" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产类型</option>
|
||||||
|
{% for type in asset_types %}
|
||||||
|
{% ifequal type.0|int2str asset_type %}
|
||||||
|
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ type.0 }}"> {{ type.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="status" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产状态</option>
|
||||||
|
{% for s in asset_status %}
|
||||||
|
{% ifequal s.0|int2str status %}
|
||||||
|
<option value="{{ s.0 }}" selected> {{ s.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ s.0 }}"> {{ s.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-4" style="padding-right: 0">
|
||||||
|
<div class="input-group inline-group">
|
||||||
|
<input type="text" class="form-control m-b input-sm" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<input type="text" style="display: none">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' href="{% url 'asset_list' %}?search=true" type="button" class="btn btn-sm btn-primary search-btn" onclick="change_info()">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
<button type="button" href="{% url 'asset_list' %}?export=true" name="export" class="btn btn-sm btn-success search-btn-excel" onclick="return false">
|
||||||
|
- 导出 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="export"></div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input id="checkall" type="checkbox" class="i-checks" name="checkall" value="checkall" data-editable='false' onclick="check_all('asset_form')">
|
||||||
|
</th>
|
||||||
|
<th class="text-center"> 主机名 </th>
|
||||||
|
<th class="text-center" name="ip"> IP地址 </th>
|
||||||
|
<th class="text-center"> IDC </th>
|
||||||
|
<th class="text-center"> 所属主机组 </th>
|
||||||
|
{# <th class="text-center"> 配置信息 </th>#}
|
||||||
|
<th class="text-center"> 操作系统 </th>
|
||||||
|
<th class="text-center"> cpu核数 </th>
|
||||||
|
<th class="text-center"> 内存 </th>
|
||||||
|
<th class="text-center"> 硬盘 </th>
|
||||||
|
<th class="text-center"> 操作 </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset in assets.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
|
||||||
|
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
|
||||||
|
</td>
|
||||||
|
<td class="text-center hostname"> <a href="{% url 'asset_detail' %}?id={{ asset.id }}">{{ asset.hostname|default_if_none:"" }}</a></td>
|
||||||
|
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
|
||||||
|
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
|
||||||
|
<td class="text-center">{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
|
||||||
|
<td class="text-center"> {{ asset.cpu|get_cpu_core|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.memory|default_if_none:"" }}{% if asset.memory %}G{% endif %} </td>
|
||||||
|
<td class="text-center"> {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
<td class="text-center" data-editable='false'>
|
||||||
|
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="button" id="exec_cmd" class="btn btn-sm btn-primary" name="exec_cmd" value="执行命令"/>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.asset_del').click(function(){
|
||||||
|
var row = $(this).closest('tr');
|
||||||
|
if (confirm("确定删除?")) {
|
||||||
|
$.get(
|
||||||
|
$(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function (data) {
|
||||||
|
row.remove()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#exec_cmd').click(function(){
|
||||||
|
var url = '{% url "role_get" %}';
|
||||||
|
var new_url = '{% url "exec_cmd" %}?role=';
|
||||||
|
var check_array = [];
|
||||||
|
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
|
||||||
|
check_array.push($(this).text())
|
||||||
|
});
|
||||||
|
check_assets = check_array.join(':');
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (dataArray.length == 1 && data != 'error'){
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['725px', '600px'],
|
||||||
|
content: new_url+data+'&check_assets='+check_assets
|
||||||
|
});
|
||||||
|
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
|
||||||
|
} else if (dataArray.length == '1' && data == 'error'){
|
||||||
|
layer.alert('没有授权系统用户')
|
||||||
|
} else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpenExec(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + '&check_assets=' + check_assets + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '授权多个系统用户,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.conn').click(function(){
|
||||||
|
var url='{% url "role_get" %}?id=' + $(this).attr('value'); // 获取用户有权限的角色
|
||||||
|
var href = $(this).attr('href');
|
||||||
|
var new_url = '{% url "terminal" %}?id=' + $(this).attr('value') + '&role='; // webterminal socket url
|
||||||
|
var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (data == 'error' || data == '' || data == null || data == undefined){
|
||||||
|
layer.alert('没有授权系统用户')
|
||||||
|
}
|
||||||
|
else if (dataArray.length == 1 && data != 'error' && navigator.platform == 'Win32'){
|
||||||
|
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['628px', '420px'],
|
||||||
|
content: new_url+data
|
||||||
|
});
|
||||||
|
} else if (dataArray.length == 1 && data != 'error'){
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
content: new_url+data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
console.log(aUrl);
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '授权多个系统用户,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function windowOpen(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var hostname = $(a).attr('value');
|
||||||
|
var title = 'Jumpserver Web Terminal - ' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
if (navigator.platform == 'Win32'){
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['628px', '420px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function windowOpenExec(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['725px', '600px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
console.log(new_url);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".iframe").on('click', function(){
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
alert("请至少选择一行!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var url= $(this).attr("value") + '?asset_id_all=' + asset_id_all;
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: 'JumpServer - 批量修改主机',
|
||||||
|
maxmin: true,
|
||||||
|
shift: 'top',
|
||||||
|
border: [2, 0.3, '#1AB394'],
|
||||||
|
shade: [0.5, '#000000'],
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
shadeClose: true,
|
||||||
|
content: url,
|
||||||
|
cancel: function(){
|
||||||
|
location.replace(location.href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.search-btn-excel').unbind('click').bind('click',function(){
|
||||||
|
var url= $(this).attr("href");
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
data: $("#asset_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$("#export").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function change_info(){
|
||||||
|
var args = $("#asset_form").serialize();
|
||||||
|
window.location = "{% url 'asset_list' %}?" + args
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#search_input").keydown(function(e){
|
||||||
|
if(e.keyCode==13){
|
||||||
|
change_info()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,378 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="text text-primary"><b>{{ asset.ip }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="" href="{% url 'asset_update' %}?id={{ asset.id }}">
|
||||||
|
<i class="fa fa-refresh"></i>
|
||||||
|
</a>
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content ibox-heading">
|
||||||
|
<h3>主机详细信息</h3>
|
||||||
|
<small><i class="fa fa-map-marker"></i> 此主机详细信息.</small>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机名</td>
|
||||||
|
<td>{{ asset.hostname|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">IP</td>
|
||||||
|
<td>{{ asset.ip|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">其他IP</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% if asset.other_ip %}
|
||||||
|
{% for ip in asset.other_ip|ip_str_to_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ ip }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">远控IP</td>
|
||||||
|
<td>{{ asset.remote_ip|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">端口</td>
|
||||||
|
<td>{{ asset.port|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机组</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for asset_group in asset.group.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_group.name|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">使用默认管理账号</td>
|
||||||
|
{# <td>{{ asset.use_default_auth|bool2str }}</td>#}
|
||||||
|
<td>{{ asset.use_default_auth|bool2str }} {% if not asset.use_default_auth %} <span class="text-info">{{ asset.username }}</span> {% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">机房</td>
|
||||||
|
<td>{{ asset.idc.name|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">硬件厂商型号</td>
|
||||||
|
<td>{{ asset.brand|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">CPU</td>
|
||||||
|
<td>{{ asset.cpu|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">内存</td>
|
||||||
|
<td>{{ asset.memory|default_if_none:"" }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">硬盘</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% if asset.disk %}
|
||||||
|
{% for disk, value in asset.disk|str_to_dic %}
|
||||||
|
<tr>
|
||||||
|
<td><span class="text-navy">{{ disk|default_if_none:"" }}</span>     {{ value|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">资产编号</td>
|
||||||
|
<td>{{ asset.number|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">SN</td>
|
||||||
|
<td>{{ asset.sn|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机类型</td>
|
||||||
|
<td>{{ asset.get_asset_type_display|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">系统版本</td>
|
||||||
|
<td>{{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">系统平台</td>
|
||||||
|
<td>{{ asset.system_arch|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">运行环境</td>
|
||||||
|
<td>{{ asset.get_env_display|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">机器状态</td>
|
||||||
|
<td>{{ asset.get_status_display|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">机柜号</td>
|
||||||
|
<td>{{ asset.cabinet|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">机柜位置</td>
|
||||||
|
<td>{{ asset.position|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">激活</td>
|
||||||
|
<td>{{ asset.is_active|bool2str }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">添加日期</td>
|
||||||
|
<td>{{ asset.date_added|date:"Y-m-d H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">备注</td>
|
||||||
|
<td>{{ asset.comment|default_if_none:"" }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>拥有权限的用户</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content ibox-heading">
|
||||||
|
<h3>主机所有授权的信息</h3>
|
||||||
|
<small><i class="fa fa-map-marker"></i> 包含了此主机所有授权的信息.</small>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
{% if perm_info %}
|
||||||
|
{% if user_perm %}
|
||||||
|
<table class="table">
|
||||||
|
<p>授权用户信息</p>
|
||||||
|
<td class="text-navy">授权用户</td>
|
||||||
|
<td class="text-navy">关联用户</td>
|
||||||
|
{% for perm in user_perm %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy"><a href="{% url 'user_detail' %}?id={{ perm.0.id }}">{{ perm.0 }}</a></td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% if perm.1 %}
|
||||||
|
{% for role in perm.1 %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy"><a href="{% url 'role_detail' %}?id={{ role.id }}">{{ role }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% if user_group_perm %}
|
||||||
|
<table class="table">
|
||||||
|
<p>授权用户组信息</p>
|
||||||
|
<td class="text-navy">授权用户组</td>
|
||||||
|
<td class="text-navy">组详情</td>
|
||||||
|
{% for user_group in user_group_perm %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ user_group }}</td>
|
||||||
|
<td class="text-navy"><a href="{% url 'user_group_list' %}?gid={{ user_group.id }}">详情</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user_rule_perm %}
|
||||||
|
<table class="table">
|
||||||
|
<p>授权规则信息</p>
|
||||||
|
<td class="text-navy">授权规则</td>
|
||||||
|
<td class="text-navy">详情</td>
|
||||||
|
{% for rule in user_rule_perm %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ rule }}</td>
|
||||||
|
<td class="text-navy"><a href="{% url 'rule_detail' %}?id={{ rule.id }}">详情</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<p class="text-center">(暂无)</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>主机修改记录</h5>
|
||||||
|
<a href="{% url 'asset_edit' %}?id={{ asset.id }}" data-toggle="tooltip" class="text-success pull-center" data-placement="bottom" title="修改">    点击修改</a>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content ibox-heading">
|
||||||
|
<h3>主机修改记录</h3>
|
||||||
|
<small><i class="fa fa-map-marker"></i> 包含了此主机所有历史修改记录.</small>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="feed-activity-list">
|
||||||
|
{% if asset_record %}
|
||||||
|
{% for r in asset_record %}
|
||||||
|
<div class="feed-element">
|
||||||
|
<div>
|
||||||
|
<small class="pull-right">{{ r.alert_time|naturaltime }}</small>
|
||||||
|
<strong class="text-navy">{{ r.username }}</strong>
|
||||||
|
{% for i in r.content|str_to_list %}
|
||||||
|
<div>{{ i.0 }} 由 <span class="text-success">{{ i.1|str_to_code }}</span> 改为 <span class="text-warning">{{ i.2|str_to_code }}</span></div>
|
||||||
|
{% endfor %}
|
||||||
|
<small class="text-success">{{ r.alert_time }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p class="text-center">(暂无)</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>最近一周登录记录</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content ibox-heading">
|
||||||
|
<h3>最近一周登录记录</h3>
|
||||||
|
<small><i class="fa fa-map-marker"></i> 此主机最近一周用户登录信息.</small>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content inspinia-timeline">
|
||||||
|
{% if log %}
|
||||||
|
{% for l in log %}
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-5 date">
|
||||||
|
<i class="fa fa-info-circle"></i>
|
||||||
|
<small class="text-navy">{{ l.user }}</small>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-7 content no-top-border">
|
||||||
|
<p class="m-b-xs"><strong>详细信息</strong></p>
|
||||||
|
<p>来源IP: {{ l.remote_ip }}</p>
|
||||||
|
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
|
<p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<button id="show" class="btn btn-primary btn-block m-t"><i class="fa fa-arrow-down"></i> 所有 </button>
|
||||||
|
<div id='more' style="display: none">
|
||||||
|
<br/>
|
||||||
|
{% for l in log_more %}
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-5 date">
|
||||||
|
<i class="fa fa-info-circle"></i>
|
||||||
|
<small class="text-navy">{{ l.user }}</small>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-7 content no-top-border">
|
||||||
|
<p class="m-b-xs"><strong>详细信息</strong></p>
|
||||||
|
<p>来源IP: {{ l.remote_ip }}</p>
|
||||||
|
<p>开始: {{ l.start_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
|
<p>结束: {{ l.end_time |date:"Y-m-d H:i:s" }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p class="text-center">(暂无)</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#show').css('display', 'none');
|
||||||
|
$('#more').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,241 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 修改资产基本信息 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div id="tab-1" class="ibox float-e-margins tab-pane active">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<form id="assetForm" method="post" class="form-horizontal">
|
||||||
|
|
||||||
|
{{ af.hostname|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.ip|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.other_ip|bootstrap_horizontal }}
|
||||||
|
<p class="col-sm-offset-2">Tips: 多个IP使用,号隔开</p>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.remote_ip|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.mac|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
{# <div class="hr-line-dashed"></div>#}
|
||||||
|
{# {{ af.port|bootstrap_horizontal }}#}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="j_group" class="col-sm-2 control-label">管理账号 <span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" {% if asset.use_default_auth %} checked="checked" {% endif %} id="id_use_default_auth" name="use_default_auth"><span> 使用默认 </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" id="admin_account" {% if asset.use_default_auth %} style="display: none" {% endif %}>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<label class="col-sm-2 control-label"> 管理用户名 <span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input type="text" value="{{ asset.username }}" name="username" class="form-control">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="col-sm-1 control-label"> 密码<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="password" value="" name="password" placeholder="不填写即不更改密码." class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" id="id_port">
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<label class="col-sm-2 control-label"> 端口<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" placeholder="Port" value="{{ asset.port|default_if_none:"" }}" name="port" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.group|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.idc|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.brand|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.cpu|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.memory|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.disk|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.system_type|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.system_version|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.system_arch|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.number|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.sn|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.cabinet|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.position|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.asset_type|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.env|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.status|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
{# <div class="hr-line-dashed"></div>#}
|
||||||
|
{# {{ af.is_active|bootstrap_horizontal }}#}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label"> 是否激活<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
{% ifequal asset.is_active 1 %}
|
||||||
|
<label> <input type="radio" checked="" value="1" name="is_active">激活 </label>
|
||||||
|
<label> <input type="radio" value="0" name="is_active"> 禁用</label>
|
||||||
|
{% else %}
|
||||||
|
<label> <input type="radio" value="1" name="is_active">激活 </label>
|
||||||
|
<label> <input type="radio" checked="" value="0" name="is_active"> 禁用</label>
|
||||||
|
{% endifequal %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.comment|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset"> 重置 </button>
|
||||||
|
<button class="btn btn-primary" type="submit"> 提交 </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('document').ready(function(){
|
||||||
|
$('#id_use_default_auth').click(function(){
|
||||||
|
if ($(this).is(':checked')){
|
||||||
|
$('#admin_account').css('display', 'none');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#admin_account').css('display', 'block');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var required_fields = ["id_hostname", "id_port"];
|
||||||
|
required_fields.forEach(function(field) {
|
||||||
|
$('label[for="' + field + '"]').parent().addClass("required");
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#assetForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_ip: [/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 'ip地址不正确'],
|
||||||
|
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||||
|
use_default_auth: function() {
|
||||||
|
var str1 = $("#id_use_default_auth").is(":checked");
|
||||||
|
if (str1 == true){
|
||||||
|
var decide = false;
|
||||||
|
} else {
|
||||||
|
var decide = true;
|
||||||
|
}
|
||||||
|
return decide}
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
"hostname": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "填写主机名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
rule: "required(use_default_auth)",
|
||||||
|
tip: "输入端口号",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
rule: "required(use_default_auth)",
|
||||||
|
tip: "输入用户名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
{# "password": {#}
|
||||||
|
{# rule: "required(use_default_auth)",#}
|
||||||
|
{# tip: "输入密码",#}
|
||||||
|
{# ok: "",#}
|
||||||
|
{# msg: {required: "必须填写!"}#}
|
||||||
|
{# }#}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,166 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/iCheck/custom.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/animate.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
|
<style>
|
||||||
|
body {background: #ffffff;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
{# <div class="ibox-title">#}
|
||||||
|
{# <h5 class="text-center"> 填写修改主机信息. </h5>#}
|
||||||
|
{# <div class="ibox-tools">#}
|
||||||
|
{# <a class="collapse-link">#}
|
||||||
|
{# <i class="fa fa-chevron-up"></i>#}
|
||||||
|
{# </a>#}
|
||||||
|
{# <a class="dropdown-toggle" data-toggle="dropdown" href="#">#}
|
||||||
|
{# <i class="fa fa-wrench"></i>#}
|
||||||
|
{# </a>#}
|
||||||
|
{# <ul class="dropdown-menu dropdown-user"></ul>#}
|
||||||
|
{# <a class="close-link">#}
|
||||||
|
{# <i class="fa fa-times"></i>#}
|
||||||
|
{# </a>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form class="form-horizontal" action="" id="signupForm" method="post" name="horizontal" role="form" autocomplete="off">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input id="ids" style="display: none">
|
||||||
|
{{ af.env|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.idc|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.port|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="j_group" class="col-sm-2 control-label">管理账号</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="radio" checked="" value="" id="no" name="use_default_auth" class="auth"><span> 不修改 </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="radio" id="default" name="use_default_auth" class="auth" value="default"><span> 使用默认 </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="radio" id="pass" name="use_default_auth" class="auth" value="user_passwd"><span> 用户名密码 </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" id="admin_account" style="display: none">
|
||||||
|
<label class="col-sm-2 control-label"> 管理用户名<span class="red-fonts"> *</span></label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input type="text" placeholder="Username" name="username" class="form-control">
|
||||||
|
</div>
|
||||||
|
<label class="col-sm-1 control-label"> 密码<span class="red-fonts">*</span> </label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="password" placeholder="Password" name="password" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="groups" class="col-sm-2 control-label">所属主机组</label>
|
||||||
|
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select id="groups" size="10" class="form-control m-b" multiple>
|
||||||
|
{% for asset_group in asset_group_all %}
|
||||||
|
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="btn-group" style="margin-top: 50px;">
|
||||||
|
<button type="button" class="btn btn-white" onclick="move_right('groups', 'groups_selected')"><i class="fa fa-chevron-right"></i></button>
|
||||||
|
<button type="button" class="btn btn-white" onclick="move_left('groups_selected', 'groups')"><i class="fa fa-chevron-left"></i> </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<div>
|
||||||
|
<select id="groups_selected" name="group" class="form-control m-b" size="10" multiple>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.cabinet|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
{{ af.comment|bootstrap_horizontal }}
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-5">
|
||||||
|
<button class="btn btn-white" type="submit"> 重置 </button>
|
||||||
|
<button class="btn btn-primary"> 提交 </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.auth').click(function(){
|
||||||
|
if ($(this).attr('id') == 'pass'){
|
||||||
|
$('#admin_account').css('display', 'block')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#admin_account').css('display', 'none')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
function move_left(from, to) {
|
||||||
|
$("#"+from+" option").each(function(){
|
||||||
|
if ( $(this).prop("selected") == true ) {
|
||||||
|
$("#"+to).append(this);
|
||||||
|
$(this).attr("selected",'false');
|
||||||
|
}
|
||||||
|
$(this).attr("selected",'true');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function move_right(from, to) {
|
||||||
|
$("#"+from+" option").each(function(){
|
||||||
|
if ( $(this).prop("selected") == true ) {
|
||||||
|
$("#"+to).append(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function move_all(from, to){
|
||||||
|
$("#"+from).children().each(function(){
|
||||||
|
$("#"+to).append(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock content %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="col-md-12 column">
|
||||||
|
<div class="alert alert-success alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
<strong>Nice!</strong> excel文件已生成请点击 <a href="/static/files/excels/{{ file_name }}" target="_blank" class="alert-link">下载</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,421 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins" id="all">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 主机详细信息列表</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form id="asset_form">
|
||||||
|
<div class="col-sm-1" style="padding-left: 0">
|
||||||
|
<a href="{% url 'asset_add' %}" class="btn btn-sm btn-primary "> 添加资产 </a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-7" style="padding-left: 0px">
|
||||||
|
<label>
|
||||||
|
<select name="idc" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">机房</option>
|
||||||
|
{% for idc in idc_all %}
|
||||||
|
{% ifequal idc.name idc_name %}
|
||||||
|
<option value="{{idc.name}}" selected> {{ idc.name|slice:":20" }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{idc.name}}"> {{ idc.name|slice:":20" }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="group" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">主机组</option>
|
||||||
|
{% for asset_group in asset_group_all %}
|
||||||
|
{% ifequal asset_group.name group_name %}
|
||||||
|
<option value="{{ asset_group.name }}" selected> {{ asset_group.name|slice:":20" }} </option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ asset_group.name }}"> {{ asset_group.name|slice:":20" }} </option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="asset_type" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产类型</option>
|
||||||
|
{% for type in asset_types %}
|
||||||
|
{% ifequal type.0|int2str asset_type %}
|
||||||
|
<option value="{{ type.0 }}" selected> {{ type.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ type.0 }}"> {{ type.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<select name="status" class="form-control m-b input-sm" onchange="change_info()">
|
||||||
|
<option value="">资产状态</option>
|
||||||
|
{% for s in asset_status %}
|
||||||
|
{% ifequal s.0|int2str status %}
|
||||||
|
<option value="{{ s.0 }}" selected> {{ s.1 }}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{ s.0 }}"> {{ s.1 }}</option>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-4" style="padding-right: 0">
|
||||||
|
<div class="input-group inline-group">
|
||||||
|
<input type="text" class="form-control m-b input-sm" id="search_input" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<input type="text" style="display: none">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' href="{% url 'asset_list' %}?search=true" type="button" class="btn btn-sm btn-primary search-btn" onclick="change_info()">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
<button type="button" href="{% url 'asset_list' %}?export=true" name="export" class="btn btn-sm btn-success search-btn-excel" onclick="return false">
|
||||||
|
- 导出 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="export"></div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" name="editable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input id="checkall" type="checkbox" class="i-checks" name="checkall" value="checkall" data-editable='false' onclick="check_all('asset_form')">
|
||||||
|
</th>
|
||||||
|
<th class="text-center"> 主机名 </th>
|
||||||
|
<th class="text-center" name="ip"> IP地址 </th>
|
||||||
|
<th class="text-center"> IDC </th>
|
||||||
|
<th class="text-center"> 所属主机组 </th>
|
||||||
|
{# <th class="text-center"> 配置信息 </th>#}
|
||||||
|
<th class="text-center"> 操作系统 </th>
|
||||||
|
<th class="text-center"> cpu核数 </th>
|
||||||
|
<th class="text-center"> 内存 </th>
|
||||||
|
<th class="text-center"> 硬盘 </th>
|
||||||
|
<th class="text-center"> 操作 </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset in assets.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center" name="id" value="{{ asset.id }}" data-editable='false'>
|
||||||
|
<input name="id" value="{{ asset.id }}" type="checkbox" class="i-checks">
|
||||||
|
</td>
|
||||||
|
<td class="text-center hostname"> <a href="{% url 'asset_detail' %}?id={{ asset.id }}">{{ asset.hostname|default_if_none:"" }}</a></td>
|
||||||
|
<td class="text-center"> {{ asset.ip|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.idc.name|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center">{{ asset.group.all|group_str2 }}</td>
|
||||||
|
{# <td class="text-center">{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}</td>#}
|
||||||
|
<td class="text-center">{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }}</td>
|
||||||
|
<td class="text-center"> {{ asset.cpu|get_cpu_core|default_if_none:"" }} </td>
|
||||||
|
<td class="text-center"> {{ asset.memory|default_if_none:"" }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
<td class="text-center"> {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %}</td>
|
||||||
|
<td class="text-center" data-editable='false'>
|
||||||
|
<a href="{% url 'asset_edit' %}?id={{ asset.id }}" class="btn btn-xs btn-info">编辑</a>
|
||||||
|
<a value="{{ asset.id }}" class="conn btn btn-xs btn-warning">连接</a>
|
||||||
|
<a value="{% url 'asset_del' %}?id={{ asset.id }}" class="btn btn-xs btn-danger asset_del">删除</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="button" id="asset_del" class="btn btn-danger btn-sm" name="del_button" value="删除"/>
|
||||||
|
<a value="{% url 'asset_edit_batch' %}" type="button" class="btn btn-sm btn-warning iframe">修改</a>
|
||||||
|
<input type="button" id="asset_update" class="btn btn-info btn-sm" name="update_button" value="更新"/>
|
||||||
|
{# <input type="button" id="asset_update_all" class="btn btn-primary btn-sm" name="update_button" value="更新全部"/>#}
|
||||||
|
<input type="button" id="exec_cmd" class="btn btn-sm btn-primary" name="exec_cmd" value="执行命令"/>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.asset_del').click(function(){
|
||||||
|
var row = $(this).closest('tr');
|
||||||
|
if (confirm("确定删除?")) {
|
||||||
|
$.get(
|
||||||
|
$(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function (data) {
|
||||||
|
row.remove()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#exec_cmd').click(function(){
|
||||||
|
var url = '{% url "role_get" %}';
|
||||||
|
var new_url = '{% url "exec_cmd" %}?role=';
|
||||||
|
var check_array = [];
|
||||||
|
$(".gradeX input:checked").closest('tr').find('.hostname a').each(function() {
|
||||||
|
check_array.push($(this).text())
|
||||||
|
});
|
||||||
|
check_assets = check_array.join(':');
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (dataArray.length == 1 && data != 'error'){
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['725px', '600px'],
|
||||||
|
content: new_url+data+'&check_assets='+check_assets
|
||||||
|
});
|
||||||
|
//window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no');
|
||||||
|
} else if (dataArray.length == '1' && data == 'error'){
|
||||||
|
layer.alert('没有授权系统用户')
|
||||||
|
} else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpenExec(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + '&check_assets=' + check_assets + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '授权多个系统用户,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.conn').click(function(){
|
||||||
|
var url='{% url "role_get" %}?id=' + $(this).attr('value'); // 获取用户有权限的角色
|
||||||
|
var href = $(this).attr('href');
|
||||||
|
var new_url = '{% url "terminal" %}?id=' + $(this).attr('value') + '&role='; // webterminal socket url
|
||||||
|
var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML;
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: url,
|
||||||
|
data: {},
|
||||||
|
success: function(data){
|
||||||
|
var dataArray = data.split(',');
|
||||||
|
if (data == 'error' || data == '' || data == null || data == undefined){
|
||||||
|
layer.alert('没有授权系统用户')
|
||||||
|
}
|
||||||
|
else if (dataArray.length == 1 && data != 'error' && navigator.platform == 'Win32'){
|
||||||
|
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['628px', '420px'],
|
||||||
|
content: new_url+data
|
||||||
|
});
|
||||||
|
} else if (dataArray.length == 1 && data != 'error'){
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
shade: false,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
content: new_url+data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aUrl = '';
|
||||||
|
$.each(dataArray, function(index, value){
|
||||||
|
aUrl += '<a onclick="windowOpen(this); return false" class="btn btn-xs btn-primary newa" href=' + new_url + value + ' value=' + hostname + '>' + value + '</a> '
|
||||||
|
});
|
||||||
|
console.log(aUrl);
|
||||||
|
layer.alert(aUrl, {
|
||||||
|
skin: 'layui-layer-molv',
|
||||||
|
title: '授权多个系统用户,请选择一个连接',
|
||||||
|
shade: false,
|
||||||
|
closeBtn: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function windowOpen(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var hostname = $(a).attr('value');
|
||||||
|
var title = 'Jumpserver Web Terminal - ' + '<span class="text-info"> '+ hostname +'</span>';
|
||||||
|
if (navigator.platform == 'Win32'){
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['628px', '420px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['628px', '452px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function windowOpenExec(a){
|
||||||
|
var new_url = $(a).attr('href');
|
||||||
|
var title = 'Jumpserver Exec Terminal';
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: title,
|
||||||
|
maxmin: true,
|
||||||
|
area: ['725px', '600px'],
|
||||||
|
shade: false,
|
||||||
|
content: new_url
|
||||||
|
});
|
||||||
|
console.log(new_url);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".iframe").on('click', function(){
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
alert("请至少选择一行!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var url= $(this).attr("value") + '?asset_id_all=' + asset_id_all;
|
||||||
|
parent.layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: 'JumpServer - 批量修改主机',
|
||||||
|
maxmin: true,
|
||||||
|
shift: 'top',
|
||||||
|
border: [2, 0.3, '#1AB394'],
|
||||||
|
shade: [0.5, '#000000'],
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
shadeClose: true,
|
||||||
|
content: url,
|
||||||
|
cancel: function(){
|
||||||
|
location.replace(location.href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.search-btn-excel').unbind('click').bind('click',function(){
|
||||||
|
var url= $(this).attr("href");
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
data: $("#asset_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$("#export").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#asset_del').click(function () {
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
alert("请至少选择一行!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (confirm("确定删除?")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
data: {asset_id_all: asset_id_all},
|
||||||
|
url: "{% url 'asset_del' %}?arg=batch",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#asset_update').click(function () {
|
||||||
|
var asset_id_all = getIDall();
|
||||||
|
if (asset_id_all == ''){
|
||||||
|
if (confirm("更新全部资产信息?")) {
|
||||||
|
layer.msg('玩命更新中...', {time: 200000});
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
url: "{% url 'asset_update_batch' %}?arg=all",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
layer.msg('玩命更新中...', {time: 200000});
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
data: {asset_id_all: asset_id_all},
|
||||||
|
url: "{% url 'asset_update_batch' %}",
|
||||||
|
success: function () {
|
||||||
|
parent.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{# $('#asset_update_all').click(function () {#}
|
||||||
|
{# layer.msg('玩命更新中...', {time: 200000});#}
|
||||||
|
{# $.ajax({#}
|
||||||
|
{# type: "post",#}
|
||||||
|
{# url: "/jasset/asset_update_batch/?arg=all",#}
|
||||||
|
{# success: function () {#}
|
||||||
|
{# parent.location.reload();#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{# });#}
|
||||||
|
|
||||||
|
function change_info(){
|
||||||
|
var args = $("#asset_form").serialize();
|
||||||
|
window.location = "{% url 'asset_list' %}?" + args
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#search_input").keydown(function(e){
|
||||||
|
if(e.keyCode==13){
|
||||||
|
change_info()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/iCheck/custom.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/animate.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
|
<style>
|
||||||
|
body {background: #ffffff;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% block content %}
|
||||||
|
<body onload="closeWindow();">
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<div class="ibox-content">
|
||||||
|
<h2 id="jumpTo" class="text-center text-info"></h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var time=2;
|
||||||
|
function closeWindow(){
|
||||||
|
window.setTimeout('closeWindow()',1000);
|
||||||
|
if(time>0){
|
||||||
|
document.getElementById("jumpTo").innerHTML="修改成功, <font color=red>"+time+"</font>秒后关闭当前窗口";
|
||||||
|
time--;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
window.parent.location.reload();
|
||||||
|
var index = parent.layer.getFrameIndex(window.name);
|
||||||
|
parent.layer.close(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock content %}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% for field in af_form %}
|
||||||
|
<div class="alert alert-warning text-center"> {{ field.errors }}</div>
|
||||||
|
{{ field.label_tag }}: {{ field }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if af.errors %}
|
||||||
|
<ul>
|
||||||
|
{% for error in af.errors %}
|
||||||
|
<li><strong>{{ error }}</strong></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,111 @@
|
||||||
|
<html>
|
||||||
|
<head>{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<input type="button" value="Play/Pause" onclick="pause(false);" />
|
||||||
|
<input type="button" value="Restart" onclick="restart(1);" />
|
||||||
|
<span id="beforeScrubberText"></span>
|
||||||
|
<input id="scrubber" type="range" value="0" min=0 max=100
|
||||||
|
onmousedown="pause(true);" onmouseup="scrub();" />
|
||||||
|
<span id="afterScrubberText"></span>
|
||||||
|
-5x <input id="speed" type="range" value="0" min=-5 max=5
|
||||||
|
onmouseup="setSpeed();" /> +5x
|
||||||
|
<script>
|
||||||
|
var data = {{ json }};
|
||||||
|
var toggle = true;
|
||||||
|
var totalTime = 0;
|
||||||
|
var TICK = 33;
|
||||||
|
var TIMESTEP = 33;
|
||||||
|
var time = 33;
|
||||||
|
var pos = 0;
|
||||||
|
var timer;
|
||||||
|
|
||||||
|
// Thanks http://stackoverflow.com/a/2998822
|
||||||
|
function zeroPad(num, size) {
|
||||||
|
var s = "0" + num;
|
||||||
|
return s.substr(s.length-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrub() {
|
||||||
|
setPercent = document.getElementById('scrubber').value;
|
||||||
|
time = (setPercent / 100) * totalTime;
|
||||||
|
restart(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTimeString(millis) {
|
||||||
|
hours = zeroPad(Math.floor(millis / (1000 * 60 * 60)), 2);
|
||||||
|
millis -= hours * (1000 * 60 * 60)
|
||||||
|
minutes = zeroPad(Math.floor(millis / (1000 * 60)), 2);
|
||||||
|
millis -= minutes * (1000 * 60);
|
||||||
|
seconds = zeroPad(Math.floor(millis / 1000), 2);
|
||||||
|
return hours + ':' + minutes + ':' + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function advance() {
|
||||||
|
document.getElementById('scrubber').value =
|
||||||
|
Math.ceil((time / totalTime) * 100);
|
||||||
|
timestr = buildTimeString(time);
|
||||||
|
document.getElementById("beforeScrubberText").innerHTML =
|
||||||
|
timestr;
|
||||||
|
for (; pos < data.length; pos++) {
|
||||||
|
if (data[pos][1] <= time) {
|
||||||
|
term.write(eval(data[pos][0]));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos >= data.length) {
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
time += TIMESTEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause(test) {
|
||||||
|
if (!toggle && test) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (toggle) {
|
||||||
|
clearInterval(timer);
|
||||||
|
toggle = !toggle;
|
||||||
|
} else {
|
||||||
|
timer = setInterval(advance, TICK);
|
||||||
|
toggle = !toggle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpeed() {
|
||||||
|
speed = document.getElementById('speed').value;
|
||||||
|
if (speed == 0) {
|
||||||
|
TIMESTEP = TICK;
|
||||||
|
} else if (speed < 0) {
|
||||||
|
TIMESTEP = TICK / -speed;
|
||||||
|
} else {
|
||||||
|
TIMESTEP = TICK * speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart(millis) {
|
||||||
|
clearInterval(timer);
|
||||||
|
term.reset();
|
||||||
|
time = millis;
|
||||||
|
pos = 0;
|
||||||
|
toggle = true;
|
||||||
|
timer = setInterval(advance, TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
var term = new Terminal({
|
||||||
|
cols: {{ dimensions[1] }},
|
||||||
|
rows: {{ dimensions[0] }},
|
||||||
|
screenKeys: true
|
||||||
|
});
|
||||||
|
totalTime = data[data.length - 1][1];
|
||||||
|
timestr = buildTimeString(totalTime);
|
||||||
|
document.getElementById("afterScrubberText").innerHTML = timestr;
|
||||||
|
term.open(document.body);
|
||||||
|
timer = setInterval(advance, TICK);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{% extends "base.jinja2" %}
|
||||||
|
{% block head %}
|
||||||
|
<script src='term.js'></script>
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Ubuntu+Mono' rel='stylesheet' type='text/css'>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Ubuntu Mono', Courier, monospace;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,126 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ log.id }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">ID</td>
|
||||||
|
<td>{{ log.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">用户名</td>
|
||||||
|
<td>{{ log.user }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">来源IP</td>
|
||||||
|
<td>{{ log.remote_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">日期</td>
|
||||||
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for asset_name in assets_hostname %}
|
||||||
|
{% if asset_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>结果</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content inspinia-timeline">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">命令</td>
|
||||||
|
<td>{{ log.cmd }}</td>
|
||||||
|
</tr>
|
||||||
|
{% for result, info in result.items %}
|
||||||
|
{% for host, msg in info.items %}
|
||||||
|
{% ifequal result 'failed' %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#last').css('display', 'none');
|
||||||
|
$('#all').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,140 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ log.id }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">ID</td>
|
||||||
|
<td>{{ log.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">用户名</td>
|
||||||
|
<td>{{ log.user }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">来源IP</td>
|
||||||
|
<td>{{ log.remote_ip }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">类型</td>
|
||||||
|
<td>{{ log.type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">日期</td>
|
||||||
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">文件</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for file_name in file_list %}
|
||||||
|
{% if file_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ file_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">主机</td>
|
||||||
|
<td>
|
||||||
|
<table class="table">
|
||||||
|
{% for asset_name in assets_hostname %}
|
||||||
|
{% if asset_name %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>结果</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content inspinia-timeline">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
{% for result, info in result.items %}
|
||||||
|
{% for host, msg in info.items %}
|
||||||
|
{% ifequal result 'failed' %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy" style="color: #ed5565">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">{{ host }}</td>
|
||||||
|
<td>{{ msg }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#last').css('display', 'none');
|
||||||
|
$('#all').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,106 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 批量命令日志 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li><a href="{% url 'log_list' 'online' %}" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||||
|
<li><a href="{% url 'log_list' 'offline' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 登录历史</a></li>
|
||||||
|
<li class="active"><a href="{% url 'log_list' 'exec' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
|
||||||
|
<li><a href="{% url 'log_list' 'file' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
|
||||||
|
<div class="" style="float: right">
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
-搜索-
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<table class="table table-striped table-bordered table-hover ">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"> ID </th>
|
||||||
|
<th class="text-center"> 用户名 </th>
|
||||||
|
<th class="text-center"> 主机 </th>
|
||||||
|
<th class="text-center"> 命令 </th>
|
||||||
|
<th class="text-center"> 来源IP </th>
|
||||||
|
<th class="text-center"> 时间 </th>
|
||||||
|
<th class="text-center"> 详情 </th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for post in contacts.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center"> {{ post.id }} </td>
|
||||||
|
<td class="text-center username"> {{ post.user }} </td>
|
||||||
|
<td class="text-center ip"> {{ post.host | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center ip"> {{ post.cmd | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center remote_ip"> {{ post.remote_ip }} </td>
|
||||||
|
<td class="text-center start_time"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'log_detail' 'exec' %}?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log_search(){
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "/jlog/search/?env=offline",
|
||||||
|
data: $("#search_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$(".tab-content").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,108 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div id="ibox-content" class="ibox-title">
|
||||||
|
<h5> 上传下载日志 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li><a href="{% url 'log_list' 'online' %}" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||||
|
<li><a href="{% url 'log_list' 'offline' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 登录历史</a></li>
|
||||||
|
<li><a href="{% url 'log_list' 'exec' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 命令记录 </a></li>
|
||||||
|
<li class="active"><a href="{% url 'log_list' 'file' %}" class="text-center"><i class="fa fa-bar-chart-o"></i> 上传下载 </a></li>
|
||||||
|
<div class="" style="float: right">
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="keyword" name="keyword" value="{{ keyword }}" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
-搜索-
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<table class="table table-striped table-bordered table-hover ">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"> ID </th>
|
||||||
|
<th class="text-center"> 用户名 </th>
|
||||||
|
<th class="text-center"> 主机 </th>
|
||||||
|
<th class="text-center"> 文件 </th>
|
||||||
|
<th class="text-center"> 类型 </th>
|
||||||
|
<th class="text-center"> 来源IP </th>
|
||||||
|
<th class="text-center"> 时间 </th>
|
||||||
|
<th class="text-center"> 详情 </th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for post in contacts.object_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center"> {{ post.id }} </td>
|
||||||
|
<td class="text-center"> {{ post.user }} </td>
|
||||||
|
<td class="text-center"> {{ post.host | truncatechars:30 }} </td>
|
||||||
|
<td class="text-center"> {{ post.filename | truncatechars:20 }} </td>
|
||||||
|
<td class="text-center"> {{ post.type }} </td>
|
||||||
|
<td class="text-center"> {{ post.remote_ip }} </td>
|
||||||
|
<td class="text-center"> {{ post.datetime|date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'log_detail' 'file' %}?id={{ post.id }}" class="btn btn-xs btn-primary">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log_search(){
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: "/jlog/search/?env=offline",
|
||||||
|
data: $("#search_form").serialize(),
|
||||||
|
success: function (data) {
|
||||||
|
$(".tab-content").html(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,117 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Jumpserver web terminal</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal {
|
||||||
|
border: #000 solid 5px;
|
||||||
|
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #f0f0f0;
|
||||||
|
background: #000;
|
||||||
|
width: 600px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reverse-video {
|
||||||
|
color: #000;
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div id="term">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="/static/js/jquery-2.1.1.js">
|
||||||
|
</script>
|
||||||
|
<script type="application/javascript" src="/static/js/term.js">
|
||||||
|
</script>
|
||||||
|
<script type="application/javascript">
|
||||||
|
function WSSHClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WSSHClient.prototype.connect = function(options) {
|
||||||
|
var endpoint = '{{ web_terminal_uri }}';
|
||||||
|
|
||||||
|
if (window.WebSocket) {
|
||||||
|
this._connection = new WebSocket(endpoint);
|
||||||
|
}
|
||||||
|
else if (window.MozWebSocket) {
|
||||||
|
this._connection = MozWebSocket(endpoint);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.onError('WebSocket Not Supported');
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._connection.onopen = function() {
|
||||||
|
options.onConnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
this._connection.onmessage = function (evt) {
|
||||||
|
var data = JSON.parse(evt.data.toString());
|
||||||
|
if (data.error !== undefined) {
|
||||||
|
options.onError(data.error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
options.onData(data.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._connection.onclose = function(evt) {
|
||||||
|
options.onClose();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
WSSHClient.prototype.send = function(data) {
|
||||||
|
this._connection.send(JSON.stringify({'data': data}));
|
||||||
|
};
|
||||||
|
|
||||||
|
function openTerminal(options) {
|
||||||
|
var client = new WSSHClient();
|
||||||
|
var term = new Terminal(80, 24, function(key) {
|
||||||
|
client.send(key);
|
||||||
|
});
|
||||||
|
term.open();
|
||||||
|
$('.terminal').detach().appendTo('#term');
|
||||||
|
term.resize(80, 24);
|
||||||
|
term.write('Connecting...');
|
||||||
|
client.connect($.extend(options, {
|
||||||
|
onError: function(error) {
|
||||||
|
term.write('Error: ' + error + '\r\n');
|
||||||
|
},
|
||||||
|
onConnect: function() {
|
||||||
|
// Erase our connecting message
|
||||||
|
term.write('\r');
|
||||||
|
},
|
||||||
|
onClose: function() {
|
||||||
|
term.write('Connection Reset By Peer');
|
||||||
|
},
|
||||||
|
onData: function(data) {
|
||||||
|
term.write(data);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type='application/javascript'>
|
||||||
|
$(document).ready(function() {
|
||||||
|
var options = {
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#ssh').show();
|
||||||
|
openTerminal(options);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,136 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="roleForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_name" class="col-sm-2 control-label">用户名称<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_name" name="role_name" placeholder="Role Name" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_password" class="col-sm-2 control-label">用户密码</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_password" name="role_password" placeholder="Role Password" type="password" class="form-control">
|
||||||
|
<span class="help-block m-b-none">如果不添加密码,会自动生成</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_key" class="col-sm-2 control-label">用户密钥</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea class="form-control" name="role_key" placeholder="请复制粘贴私钥" rows="10" style="font-size: 9px;"></textarea>
|
||||||
|
<span class="help-block m-b-none">如果不添加密钥,会自动生成 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo" class="col-sm-2 control-label">关联Sudo</label>
|
||||||
|
<div class="col-sm-8" id="sudo_name">
|
||||||
|
<select name="sudo_name" data-placeholder="请选择Sudo别名" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for sudo in sudos %}
|
||||||
|
<option value="{{ sudo.id }}">{{ sudo.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_comment" name="role_comment" placeholder="Role Comment" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#roleForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
||||||
|
{# either: function(){#}
|
||||||
|
{# return $('#role_password').val() == ''#}
|
||||||
|
{# }#}
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"role_name": {
|
||||||
|
rule: "required;check_name",
|
||||||
|
tip: "输入系统用户名称",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "系统用户名称必填"}
|
||||||
|
},
|
||||||
|
{# "role_key": {#}
|
||||||
|
{# rule: "required(either)",#}
|
||||||
|
{# tip: "输入密钥",#}
|
||||||
|
{# ok: "",#}
|
||||||
|
{# msg: {required: "密码和密钥必填一个!"}#}
|
||||||
|
{# }#}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ role.name }} - 授权规则</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table" id="ugedit" >
|
||||||
|
<td class="text-navy text-left">时间</td>
|
||||||
|
<td class="text-navy text-right">名称</td>
|
||||||
|
{% for rule in rules %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-left"> {{ rule.date_added | date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-right"> <a href="{% url 'rule_detail' %}?id={{ rule.id }}">{{ rule.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>授权用户/用户组</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="text-center">
|
||||||
|
<table class="table" id="agedit" >
|
||||||
|
<td class="text-navy text-left">用户</td>
|
||||||
|
<td class="text-navy text-right">用户组</td>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-left">
|
||||||
|
{% for user in users %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td> <a href="{% url 'user_detail' %}?id={{ user.id }}">{{ user.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-right">
|
||||||
|
{% for group in user_groups %}
|
||||||
|
<tr class="gradeX-">
|
||||||
|
<td> <a href="{% url 'user_group_list' %}?id={{ group.id }}">{{ group.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>授权主机组/主机组</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="text-center">
|
||||||
|
<table class="table" id="agedit" >
|
||||||
|
<td class="text-navy text-left">主机</td>
|
||||||
|
<td class="text-navy text-right">主机组</td>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-left">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td> <a href="{% url 'asset_detail' %}?id={{ asset.id }}">{{ asset.ip }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-right">
|
||||||
|
{% for group in asset_groups %}
|
||||||
|
<tr class="gradeX-">
|
||||||
|
<td> <a href="{% url 'asset_list' %}?group_id={{ group.id }}">{{ group.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ role.name }} - 推送主机</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table table-striped" id="ugedit" >
|
||||||
|
<a class="btn btn-xs btn-danger del_muti"> 删除 </a><span> </span>
|
||||||
|
<a class="btn btn-xs btn-primary re_push"> 重新推送 </a>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_push" onclick="checkAll('check_push', 'asset_id')">
|
||||||
|
</th>
|
||||||
|
<th class="text-center">主机</th>
|
||||||
|
<th class="text-center">密钥</th>
|
||||||
|
<th class="text-center">密码</th>
|
||||||
|
<th class="text-center">结果</th>
|
||||||
|
<th class="text-center">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset, info in pushed_asset.items %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" name="asset_id" value="{{ asset.id }}">
|
||||||
|
</th>
|
||||||
|
<td class="text-center"> {{ asset.hostname }} </td>
|
||||||
|
<td class="text-center"> {{ info.key | yesno:"是,否,未知" }} </td>
|
||||||
|
<td class="text-center"> {{ info.password | yesno:"是,否,未知" }} </td>
|
||||||
|
{% if info.success %}
|
||||||
|
<td class="text-center" style="color: #1ab394;" >{{ info.success | yesno:"成功,失败,未知" }} </td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-center" style="color: #ec4758;cursor: help" title="{{ info.result }}" >{{ info.success | yesno:"成功,失败,未知" }} </td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="text-center" ><a class="fa fa-times del" href="{% url 'role_recycle' %}?role_id={{ role.id }}&asset_id={{ asset.id }}" style="color: #ec4758;"></a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-danger"><b>{{ role.name }} - 未推送主机</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table table-striped" >
|
||||||
|
<a class="btn btn-xs btn-primary push_muti"> 推送 </a>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_no_push" onclick="checkAll('check_no_push', 'asset_no_push_id')">
|
||||||
|
</th>
|
||||||
|
<th class="text-center">主机</th>
|
||||||
|
<th class="text-center">IP</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset in need_push_asset %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" name="asset_no_push_id" value="{{ asset.id }}">
|
||||||
|
</th>
|
||||||
|
<td class="text-center"> {{ asset.hostname }} </td>
|
||||||
|
<td class="text-center"> {{ asset.ip }} </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.del').click(function(){
|
||||||
|
var url = $(this).attr('href');
|
||||||
|
$.get(
|
||||||
|
url,
|
||||||
|
{},
|
||||||
|
function(data){
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.del_muti').click(function(){
|
||||||
|
var check_array = [];
|
||||||
|
if (confirm("确定删除")) {
|
||||||
|
$(".gradeX input[name='asset_id']:checked").each(function() {
|
||||||
|
check_array.push($(this).attr("value"))
|
||||||
|
});
|
||||||
|
var url = '/jperm/role/recycle/?role_id={{ role.id }}&asset_id=' + check_array.join(',');
|
||||||
|
console.log(check_array);
|
||||||
|
$.get(url,
|
||||||
|
{},
|
||||||
|
function(data){
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.push_muti').click(function(){
|
||||||
|
var check_array = [];
|
||||||
|
$(".gradeX input[name='asset_no_push_id']:checked").each(function() {
|
||||||
|
check_array.push($(this).attr("value"))
|
||||||
|
});
|
||||||
|
var url = '/jperm/role/push/?id={{ role.id }}&asset_id=' + check_array.join(',');
|
||||||
|
$(this).attr('href', url)
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.re_push').click(function(){
|
||||||
|
var check_array = [];
|
||||||
|
$(".gradeX input[name='asset_id']:checked").each(function() {
|
||||||
|
check_array.push($(this).attr("value"))
|
||||||
|
});
|
||||||
|
var url = '/jperm/role/push/?id={{ role.id }}&asset_id=' + check_array.join(',');
|
||||||
|
$(this).attr('href', url)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,129 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="roleForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_name" class="col-sm-2 control-label">用户名称<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_name" name="role_name" placeholder="Role Name" type="text" class="form-control" value="{{ role.name }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_password" class="col-sm-2 control-label">用户密码</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_password" name="role_password" type="password" class="form-control">
|
||||||
|
<span class="help-block m-b-none">不修改请留空</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_key" class="col-sm-2 control-label">用户密钥</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea class="form-control" name="role_key" placeholder="请复制粘贴私钥" rows="10" style="font-size: 9px;"></textarea>
|
||||||
|
<span class="help-block m-b-none">不修改请留空</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo" class="col-sm-2 control-label">关联sudo<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="sudo_name" data-placeholder="请选择Sudo" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for sudo in sudo_all %}
|
||||||
|
<option value="{{ sudo.id }}" {% if sudo in role_sudos %} selected {% endif %}>{{ sudo.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role_comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="role_comment" name="role_comment" placeholder="Role Comment" type="text" class="form-control" value="{{ role.comment }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#roleForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位']
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"role_name": {
|
||||||
|
rule: "required;check_name",
|
||||||
|
tip: "输入系统用户名称",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "系统用户名称必填"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div>
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 所有系统用户</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="">
|
||||||
|
<a href="{% url 'role_add' %}" class="btn btn-sm btn-primary "> 添加系统用户 </a>
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">名称 </th>
|
||||||
|
<th class="text-center">sudo别名</th>
|
||||||
|
<th class="text-center">创建时间</th>
|
||||||
|
<th class="text-center">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="edittbody">
|
||||||
|
{% for role in roles %}
|
||||||
|
<tr class="gradeX" id={{ role.id }}>
|
||||||
|
<td class="text-center"><a href="{% url 'role_detail' %}?id={{ role.id }}">{{ role.name }} </a></td>
|
||||||
|
<td class="text-center"> {{ role | role_contain_which_sudos }} </td>
|
||||||
|
<td class="text-center"> {{ role.date_added | date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'role_edit' %}?id={{ role.id }}" class="btn btn-xs btn-info">编辑</a>
|
||||||
|
<a href="{% url 'role_push' %}?id={{ role.id }}" class="btn btn-xs btn-warning">推送</a>
|
||||||
|
<button onclick="remove_role({{ role.id }})" class="btn btn-xs btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
|
||||||
|
Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function remove_role(role_id){
|
||||||
|
if (confirm("确认删除")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'role_del' %}",
|
||||||
|
data: "id=" + role_id,
|
||||||
|
success: function(msg){
|
||||||
|
alert( "成功: " + msg );
|
||||||
|
var del_row = $('tbody#edittbody>tr#' + role_id);
|
||||||
|
del_row.remove()
|
||||||
|
},
|
||||||
|
error: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
alert("失败: " + msg)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="pushForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role" class="col-sm-2 control-label">系统用户</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input name="id" type="text" class="form-control" disabled value="{{ role.name }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset" class="col-sm-2 control-label">资产</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="assets" id="assets" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<option value="{{ asset.id }}" {% if asset in need_push_asset %}selected{% endif %}>{{ asset.hostname }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="assetgroup" class="col-sm-2 control-label">资产组</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset_groups" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<option value="{{ asset_group.id }}"{% if asset_group in pushed_asset_groups %}selected{% endif %}>{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="j_group" class="col-sm-2 control-label">使用密钥</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="1" id="use_publicKey" name="use_publicKey" checked>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="j_group" class="col-sm-2 control-label">使用密码</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="radio i-checks">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="1" id="use_password" name="use_password">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="comment" name="comment" placeholder="Comment" type="text" class="form-control" {% if error %}value="{{ username }}" {% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">推送</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#pushForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
||||||
|
check_asset: function(){
|
||||||
|
return $('#assets').val() == null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
"asset_groups": {
|
||||||
|
rule: "required(check_asset)",
|
||||||
|
tip: "输入资产组",
|
||||||
|
msg: {required: "资产和资产组必选一个!"}
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "请选择系统用户",
|
||||||
|
msg: {required: "必须选择系统用户"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="ruleForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="name" name="name" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user" class="col-sm-2 control-label">用户</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="user" id="user" data-placeholder="请选择用户" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}">{{ user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<span class="help-block m-b-none">用户和用户组必选一个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user_group" class="col-sm-2 control-label">用户组</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="user_group" id="user_group" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for user_group in user_groups %}
|
||||||
|
<option value="{{ user_group.id }}">{{ user_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset" class="col-sm-2 control-label">资产</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset" id="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<option value="{{ asset.id }}">{{ asset.hostname }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<span class="help-block m-b-none">资产和资产组必选一个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset_group" id="asset_group" class="col-sm-2 control-label">资产组</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset_group" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role" class="col-sm-2 control-label">系统用户<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8" id="role_name">
|
||||||
|
<select name="role" data-placeholder="请选择需要关联的系统用户" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for role in roles %}
|
||||||
|
<option value="{{ role.id }}">{{ role.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="comment" name="comment" type="text" class="form-control" {% if error %}value="{{ username }}" {% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#ruleForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
||||||
|
check_user: function(){
|
||||||
|
return $('#user').val() == null
|
||||||
|
},
|
||||||
|
check_asset: function(){
|
||||||
|
return $('#asset').val() == null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"name": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "输入规则名称",
|
||||||
|
msg: {required: "规则名称必填"}
|
||||||
|
},
|
||||||
|
"user_group": {
|
||||||
|
rule: "required(check_user)",
|
||||||
|
tip: "请选择用户组",
|
||||||
|
msg: {required: "用户和用户组必选一个!"}
|
||||||
|
},
|
||||||
|
"asset_group": {
|
||||||
|
rule: "required(check_asset)",
|
||||||
|
tip: "输入资产组",
|
||||||
|
msg: {required: "资产和资产组必选一个!"}
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "请选择系统用户",
|
||||||
|
msg: {required: "必须选择系统用户"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>{{ rule.name }}</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div>
|
||||||
|
<div class="text-left">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">ID</td>
|
||||||
|
<td>{{ rule.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">名称</td>
|
||||||
|
<td>{{ rule.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">时间</td>
|
||||||
|
<td>{{ rule.date_added | date:"Y-m-d H:i:s"}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">关联用户</td>
|
||||||
|
<td>{{ roles_name }}</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-navy">激活</td>
|
||||||
|
<td>是</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>授权用户/用户组</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="text-center">
|
||||||
|
<table class="table" id="agedit" >
|
||||||
|
<td class="text-navy text-left">用户</td>
|
||||||
|
<td class="text-navy text-right">用户组</td>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-left">
|
||||||
|
{% for user in users %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td> <a href="{% url 'user_detail' %}?id={{ user.id }}">{{ user.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-right">
|
||||||
|
{% for group in user_groups %}
|
||||||
|
<tr class="gradeX-">
|
||||||
|
<td> <a href="{% url 'user_group_list' %}?group_id={{ group.id }}">{{ group.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label label-primary"><b>授权主机组/主机组</b></span>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-user">
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="text-center">
|
||||||
|
<table class="table" id="agedit" >
|
||||||
|
<td class="text-navy text-left">主机</td>
|
||||||
|
<td class="text-navy text-right">主机组</td>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-left">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td> <a href="{% url 'asset_detail' %}?id={{ asset.id }}">{{ asset.ip }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table class="table progress-striped text-right">
|
||||||
|
{% for group in asset_groups %}
|
||||||
|
<tr class="gradeX-">
|
||||||
|
<td> <a href="{% url 'asset_list' %}?group_id={{ group.id }}">{{ group.name }}</a> </td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#show').click(function(){
|
||||||
|
$('#last').css('display', 'none');
|
||||||
|
$('#all').css('display', 'block');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,179 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="ruleForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="name" name="name" placeholder="Rule Name" type="text" class="form-control" value={{ rule.name }}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user" class="col-sm-2 control-label">用户</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="user" id="user" data-placeholder="用户名" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}" {% if user in rule.user.all %} selected {% endif %}>{{ user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<span class="help-block m-b-none">用户和用户组必选一个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user_group" class="col-sm-2 control-label">用户组</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="user_group" id="user_group" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for user_group in user_groups %}
|
||||||
|
<option value="{{ user_group.id }}"{% if user_group in rule.user_group.all %} selected {% endif %}>{{ user_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset" class="col-sm-2 control-label">资产</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset" id="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<option value="{{ asset.id }}"{% if asset in rule.asset.all %} selected {% endif %}>{{ asset.ip }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<span class="help-block m-b-none">资产和资产组必选一个</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset_group" id="asset_group" class="col-sm-2 control-label">资产组</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset_group" data-placeholder="请选择资产组" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<option value="{{ asset_group.id }}"{% if asset_group in rule.asset_group.all %} selected {% endif %}>{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role" class="col-sm-2 control-label">系统用户<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="role" data-placeholder="请选择系统用户" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||||
|
{% for role in roles %}
|
||||||
|
<option value="{{ role.id }}"{% if role in rule.role.all %} selected {% endif %}>{{ role.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="comment" name="comment" placeholder="Rule Comment" type="text" class="form-control" value="{{ rule.comment }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
|
||||||
|
$('#ruleForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
||||||
|
check_user: function(){
|
||||||
|
return $('#user').val() == null
|
||||||
|
},
|
||||||
|
check_asset: function(){
|
||||||
|
return $('#asset').val() == null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"name": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "输入规则名称",
|
||||||
|
msg: {required: "规则名称必填"}
|
||||||
|
},
|
||||||
|
"user_group": {
|
||||||
|
rule: "required(check_user)",
|
||||||
|
tip: "请选择用户组",
|
||||||
|
msg: {required: "用户和用户组必选一个!"}
|
||||||
|
},
|
||||||
|
"asset_group": {
|
||||||
|
rule: "required(check_asset)",
|
||||||
|
tip: "输入资产组",
|
||||||
|
msg: {required: "资产和资产组必选一个!"}
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "请选择系统用户",
|
||||||
|
msg: {required: "必须选择系统用户"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div>
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 所有规则</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="">
|
||||||
|
<a href="{% url 'rule_add' %}" class="btn btn-sm btn-primary "> 添加规则 </a>
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">规则名称 </th>
|
||||||
|
<th class="text-center">用户</th>
|
||||||
|
<th class="text-center">用户组</th>
|
||||||
|
<th class="text-center">资产</th>
|
||||||
|
<th class="text-center">资产组</th>
|
||||||
|
<th class="text-center">系统用户</th>
|
||||||
|
<th class="text-center">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="edittbody">
|
||||||
|
{% for rule in rules %}
|
||||||
|
<tr class="gradeX" id={{ rule.id }}>
|
||||||
|
<td class="text-center"> <a href="{% url 'rule_detail' %}?id={{ rule.id }}" >{{ rule.name }}</a> </td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{ rule | rule_member_count:"user" }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{ rule | rule_member_count:"user_group" }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{ rule | rule_member_count:"asset" }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{ rule | rule_member_count:"asset_group" }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{ rule | rule_member_count:"role" }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'rule_edit' %}?id={{ rule.id }}" class="btn btn-xs btn-info">编辑</a>
|
||||||
|
<button onclick="remove_rule({{ rule.id }})" class="btn btn-xs btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
|
||||||
|
Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function remove_rule(rule_id){
|
||||||
|
if (confirm("确认删除")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'rule_del' %}",
|
||||||
|
data: "id=" + rule_id,
|
||||||
|
success: function(msg){
|
||||||
|
alert( "成功: " + msg );
|
||||||
|
var del_row = $('tbody#edittbody>tr#' + rule_id);
|
||||||
|
del_row.remove()
|
||||||
|
},
|
||||||
|
error: function (msg) {
|
||||||
|
alert("失败: " + msg)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="sudoForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_name" class="col-sm-2 control-label">命令别名<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="sudo_name" name="sudo_name" placeholder="Sudo Command Alias" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_commands" class="col-sm-2 control-label">系统命令<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea id="sudo_commands" name="sudo_commands" class="form-control" rows="3" placeholder="/bin/grep, /bin/find"></textarea>
|
||||||
|
<span class="help-block m-b-none">sudo命令,逗号分隔, 不支持换行</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="sudo_comment" name="sudo_comment" placeholder="Sudo Comment" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#sudoForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大写字母,2-20位']
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"sudo_name": {
|
||||||
|
rule: "required;check_name"
|
||||||
|
},
|
||||||
|
"sudo_runas": {
|
||||||
|
rule: "required;check_name"
|
||||||
|
},
|
||||||
|
"sudo_commands": {
|
||||||
|
rule: "required"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
<link href="/static/css/plugins/datapicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>填写基本信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="sudoForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_name" class="col-sm-2 control-label">命令别名<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="sudo_name" name="sudo_name" placeholder="Sudo Command Alias" type="text" class="form-control" value={{ sudo.name }}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_commands_label" class="col-sm-2 control-label">系统命令<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea id="sudo_commands" name="sudo_commands" class="form-control" rows="3">{{ sudo.commands }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sudo_comment" class="col-sm-2 control-label">备注</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="sudo_comment" name="sudo_comment" placeholder="Sudo Comment" type="text" class="form-control" value={{ sudo.commnet }}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("input.role").click(function(){
|
||||||
|
if($("input.role[value=GA]").is( ":checked" )){
|
||||||
|
$("#admin_groups").css("display", 'none');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$("#admin_groups").css("display", 'block');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#use_password').click(function(){
|
||||||
|
if ($(this).is(':checked')){
|
||||||
|
$('#admin_account_password').css('display', 'block')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$('#admin_account_password').css('display', 'none')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#use_publicKey').click(function(){
|
||||||
|
if ($(this).is(':checked')){
|
||||||
|
|
||||||
|
$('#admin_account_publicKey').css('display', 'block')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#admin_account_publicKey').css('display', 'none')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select' : {},
|
||||||
|
'.chosen-select-deselect' : {allow_single_deselect:true},
|
||||||
|
'.chosen-select-no-single' : {disable_search_threshold:10},
|
||||||
|
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width' : {width:"95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||||
|
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div>
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 所有Sudo命令别名</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="">
|
||||||
|
<a href="{% url 'sudo_add' %}" class="btn btn-sm btn-primary "> 添加别名 </a>
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right mail-search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
- 搜索 -
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="editable" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">命令别名 </th>
|
||||||
|
<th class="text-center">系统命令</th>
|
||||||
|
<th class="text-center">创建时间</th>
|
||||||
|
<th class="text-center">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="edittbody">
|
||||||
|
{% for sudo in sudos %}
|
||||||
|
<tr class="gradeX" id={{ sudo.id }}>
|
||||||
|
<td class="text-center"> {{ sudo.name }} </td>
|
||||||
|
<td class="text-center"> {{ sudo.commands }} </td>
|
||||||
|
<td class="text-center"> {{ sudo.date_added | date:"Y-m-d H:i:s"}} </td>
|
||||||
|
<td class="text-center">
|
||||||
|
{# <a href="/jperm/sudo/perm_sudo_detail/?id={{ sudo.id }}" class="btn btn-xs btn-primary">详情</a>#}
|
||||||
|
<a href="{% url 'sudo_edit' %}?id={{ sudo.id }}" class="btn btn-xs btn-info">编辑</a>
|
||||||
|
<button onclick="remove_sudo({{ sudo.id }})" class="btn btn-xs btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
|
||||||
|
Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'paginator.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function remove_sudo(sudo_id){
|
||||||
|
if (confirm("确认删除")) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/jperm/sudo/del/",
|
||||||
|
data: "id=" + sudo_id,
|
||||||
|
success: function(msg){
|
||||||
|
alert( "成功: " + msg );
|
||||||
|
var del_row = $('tbody#edittbody>tr#' + sudo_id);
|
||||||
|
del_row.remove()
|
||||||
|
},
|
||||||
|
error: function (msg) {
|
||||||
|
alert("失败: " + msg)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
real_file=/etc/sudoers
|
||||||
|
tmp_file=$(mktemp /tmp/XXXXXXX)
|
||||||
|
|
||||||
|
# Backup sudoers file
|
||||||
|
cp ${sudo_file} ${sudo_file_bak}
|
||||||
|
|
||||||
|
# Add Command Aliases
|
||||||
|
add_cmd_alias() {
|
||||||
|
sudo_file=$1
|
||||||
|
{% for sudo_name, sudo_cmd in sudo_alias.items %}
|
||||||
|
{% if sudo_name != 'ALL' %}
|
||||||
|
if $(grep '^Cmnd_Alias {{ sudo_name }}' ${sudo_file} &> /dev/null); then
|
||||||
|
sed -i 's@^Cmnd_Alias.*{{ sudo_name }}.*@Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}@g' ${sudo_file}
|
||||||
|
else
|
||||||
|
echo "Cmnd_Alias {{ sudo_name }} = {{ sudo_cmd }}" >> ${sudo_file}
|
||||||
|
fi
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Add Command Aliases to role
|
||||||
|
add_role_chosen() {
|
||||||
|
sudo_file=$1
|
||||||
|
{% for user, alias in sudo_user.items %}
|
||||||
|
if $(grep '^{{ user }}.*' ${sudo_file} &> /dev/null); then
|
||||||
|
sed -i 's@^{{ user }}.*@{{ user }} ALL = (root) NOPASSWD: {{ alias }}@g' ${sudo_file}
|
||||||
|
else
|
||||||
|
echo "{{ user }} ALL = (root) NOPASSWD: {{ alias }}" >> ${sudo_file}
|
||||||
|
fi
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_syntax(){
|
||||||
|
visudo -c -f $1
|
||||||
|
}
|
||||||
|
|
||||||
|
cp $real_file $tmp_file && add_cmd_alias $tmp_file && add_role_chosen $tmp_file || exit 1
|
||||||
|
check_syntax $tmp_file && add_cmd_alias $real_file && add_role_chosen $real_file && rm -f $tmp_file || exit 2
|
||||||
|
check_syntax $real_file
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5>编辑用户信息</h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="userForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="user_id" name="user_id" type="text" value="{{ user.id }}" style="display: none">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" class="col-sm-2 control-label">姓名<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="name" name="name" placeholder="Name" type="text" class="form-control" value="{{ user.name }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="col-sm-2 control-label">密码</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="password" name="password" placeholder="Password" type="password" class="form-control">
|
||||||
|
<span class="help-block m-b-none">
|
||||||
|
登陆web的密码, 不修改请留空
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ssh_key_pwd" class="col-sm-2 control-label">SSH密钥</label>
|
||||||
|
<div class="col-sm-8" style="border: none">
|
||||||
|
<a value="{% url 'key_gen' %}?uuid={{ user.uuid }}" id="regen_ssh_key" class="form-control"> 重新生成</a>
|
||||||
|
<span class="help-block m-b-none">
|
||||||
|
重新生成密钥,需要重新下载并导入
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="col-sm-2 control-label">Email<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="email" name="email" type="email" placeholder="Email" class="form-control" value="{{ user.email }}" >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="submit">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认修改</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#userForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_pass: [/^\w+$/, '数字和字符']
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"name": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "姓名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写"}
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
rule: "required",
|
||||||
|
tip: "Email",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("document").ready(function() {
|
||||||
|
$("#regen_ssh_key").click(function () {
|
||||||
|
alert('申请已提交,请等待,请勿重复提交');
|
||||||
|
$.get(
|
||||||
|
$(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function(data){
|
||||||
|
alert(data)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,54 @@
|
||||||
|
<html><head>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>忘记密码</title>
|
||||||
|
|
||||||
|
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<link href="/static/css/animate.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style type="text/css"></style></head>
|
||||||
|
|
||||||
|
<body class="gray-bg">
|
||||||
|
|
||||||
|
<div class="lock-word animated fadeInDown">
|
||||||
|
<span class="first-word">Jumpserver</span>
|
||||||
|
</div>
|
||||||
|
<div class="middle-box text-center lockscreen animated fadeInDown">
|
||||||
|
<div>
|
||||||
|
<div class="m-b-md">
|
||||||
|
{# <img alt="image" class="img-circle circle-border" src="https://s3.amazonaws.com/uifaces/faces/twitter/ok/128.jpg">#}
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<h3>忘记密码</h3>
|
||||||
|
<p>请输入您原来的信息</p>
|
||||||
|
<form class="m-t" role="form" action="" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name='username' class="form-control" placeholder="Username" required="">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name='name' class="form-control" placeholder="Name" required="">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name='email' class="form-control" placeholder="Email" required="">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary block full-width">确定</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mainly scripts -->
|
||||||
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
|
<script src="/static/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,73 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>重置{{ name }}</title>
|
||||||
|
|
||||||
|
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/animate.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style type="text/css"></style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="gray-bg">
|
||||||
|
|
||||||
|
<div class="lock-word animated fadeInDown">
|
||||||
|
<span class="first-word">Jump</span><span>Server</span>
|
||||||
|
</div>
|
||||||
|
<div class="middle-box text-center lockscreen animated fadeInDown">
|
||||||
|
<div>
|
||||||
|
<div class="m-b-md">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<h3>请输入新密码</h3>
|
||||||
|
<form class="m-t" role="form" id="restForm" action="{{ action }}" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name='password' class="form-control" placeholder="New Password">
|
||||||
|
<input type="password" name='password_confirm' class="form-control" placeholder="Password Confirm">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary block full-width">确定</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Mainly scripts -->
|
||||||
|
<script src="/static/js/jquery-2.1.1.js"></script>
|
||||||
|
<script src="/static/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/static/js/validator/jquery.validator.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$('#restForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_pass: [/^\w{8,50}$/, '数字和字符,8-50位']
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
"password": {
|
||||||
|
rule: "required;check_pass",
|
||||||
|
tip: "输入密码",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
},
|
||||||
|
'password_confirm': {
|
||||||
|
rule: "required;check_pass;",
|
||||||
|
tip: '确认密码',
|
||||||
|
msg: {required: "必须填写!"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,138 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% load mytags %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 命令批量执行 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapise-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<form method="post" id="ruleForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="asset" class="col-sm-2 control-label">资产<span class="red-fonts">*</span></label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select name="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b"
|
||||||
|
multiple tabindex="2">
|
||||||
|
{% for asset in assets %}
|
||||||
|
<option value="{{ asset.ip }}">{{ asset.ip }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">命令<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input class="form-control" name="cmd" placeholder="请输入要执行的命令">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="clean">清除</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">运行</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_head_css_js %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
<script src="{% static 'js/jquery.shiftcheckbox.js' %}"></script>
|
||||||
|
<link href="/static/css/plugins/datepicker/datepicker3.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
|
||||||
|
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
'.chosen-select': {},
|
||||||
|
'.chosen-select-deselect': {allow_single_deselect: true},
|
||||||
|
'.chosen-select-no-single': {disable_search_threshold: 10},
|
||||||
|
'.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'},
|
||||||
|
'.chosen-select-width': {width: "95%"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var selector in config) {
|
||||||
|
$(selector).chosen(config[selector]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.del').click(function(){
|
||||||
|
var row = $(this).closest('tr');
|
||||||
|
if (confirm("确定删除")) {
|
||||||
|
$.get(
|
||||||
|
$(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function(data){
|
||||||
|
row.remove();
|
||||||
|
alert(data);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#del_btn').click(function(){
|
||||||
|
var check_array = [];
|
||||||
|
if (confirm("确定删除")) {
|
||||||
|
$(".gradeX input:checked").each(function() {
|
||||||
|
check_array.push($(this).attr("value"))
|
||||||
|
});
|
||||||
|
$.post("/juser/user_del/",
|
||||||
|
{id: check_array.join(",")},
|
||||||
|
function(data){
|
||||||
|
$(".gradeX input:checked").closest("tr").remove();
|
||||||
|
alert(data);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.email').click(function(){
|
||||||
|
$.get('/juser/send_mail_retry/?uuid=' + $(this).attr('value'),
|
||||||
|
{},
|
||||||
|
function(data){
|
||||||
|
alert(data)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
$("tbody tr").shiftcheckbox({
|
||||||
|
checkboxSelector: 'input:checkbox',
|
||||||
|
selectAll: $('#select_all'),
|
||||||
|
ignoreClick: 'a'
|
||||||
|
});
|
||||||
|
$('.shiftCheckbox').shiftcheckbox();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,138 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="http://cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="http://cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" rel="stylesheet">
|
||||||
|
<script src="http://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script>
|
||||||
|
<script src="http://cdn.bootcss.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var wsUri = "ws://j:8080/send";
|
||||||
|
var ws;
|
||||||
|
function connect(){
|
||||||
|
ws = new WebSocket(wsUri);
|
||||||
|
ws.onopen = function(evt){ console.log(evt.data) };
|
||||||
|
ws.onclose = function(evt){ console.log(evt.data) };
|
||||||
|
ws.onmessage = function(evt){ console.log(evt.data) };
|
||||||
|
ws.onerror = function(evt){ console.log(evt.data) };
|
||||||
|
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
function send() {
|
||||||
|
var data = {
|
||||||
|
input:$("#input_data").val()
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('hello');
|
||||||
|
ws.send(JSON.stringify(data));
|
||||||
|
|
||||||
|
$("#message").empty();
|
||||||
|
ws.onopen = function() {
|
||||||
|
ws.send(JSON.stringify(data));
|
||||||
|
};
|
||||||
|
|
||||||
|
$("#message").append('<div class="panel-body"><p>');
|
||||||
|
ws.onmessage = function(event) {
|
||||||
|
$("#message").append(JSON.parse(event.data).input + "<br>");
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function(event) {
|
||||||
|
$("#message").append('</p></div>');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
{# function send() {#}
|
||||||
|
{# var ws = new WebSocket("ws://j:8080/send");#}
|
||||||
|
{##}
|
||||||
|
{# var data = {#}
|
||||||
|
{# input:$("#input_data").val(),#}
|
||||||
|
{# };#}
|
||||||
|
{##}
|
||||||
|
{# $("#message").empty();#}
|
||||||
|
{# ws.onopen = function() {#}
|
||||||
|
{# ws.send(JSON.stringify(data));#}
|
||||||
|
{# };#}
|
||||||
|
{##}
|
||||||
|
{# $("#message").append('<div class="panel-body"><p>');#}
|
||||||
|
{# ws.onmessage = function(event) {#}
|
||||||
|
{# $("#message").append(JSON.parse(event.data).input + "<br>");#}
|
||||||
|
{# };#}
|
||||||
|
{##}
|
||||||
|
{# ws.onclose = function(event) {#}
|
||||||
|
{# $("#message").append('</p></div>');#}
|
||||||
|
{# };#}
|
||||||
|
{# }#}
|
||||||
|
|
||||||
|
function disconnect(){
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{# <div id="test">#}
|
||||||
|
{# <form class="form-horizontal" role="form">#}
|
||||||
|
{# <div class="panel panel-default">#}
|
||||||
|
{# <div class="panel-heading">#}
|
||||||
|
{# <h5 class="panel-title">>>输入</h5>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# <div class="panel-body">#}
|
||||||
|
{# <div class="form-group">#}
|
||||||
|
{# <div class="col-md-8">#}
|
||||||
|
{# <input type="text" class="form-control" id="input_data" value="">#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# <div class="form-group">#}
|
||||||
|
{# <div class="col-md-8">#}
|
||||||
|
{# <input type="button" class="btn btn-success" id="input_1_btn" onclick="connect();" value="连接" />#}
|
||||||
|
{# <input type="button" class="btn btn-success" id="input_btn" onclick="send();" value="查看" />#}
|
||||||
|
{# <input type="button" class="btn btn-success" id="input_2_btn" onclick="close();" value="关闭" />#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </form>#}
|
||||||
|
{# <div class="panel panel-default">#}
|
||||||
|
{# <div class="panel-heading">#}
|
||||||
|
{# <h5 class="panel-title">>> 输出</h5>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# <div class="panel-body">#}
|
||||||
|
{# <div id="message"></div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title" style="border: solid">
|
||||||
|
<h5> 实时监控 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapise-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content blank-panel" style="background-color: #0b0b0b; color: #006621; height: 500px; padding: 20px;">
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好你好你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
你好<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</html>
|
|
@ -0,0 +1,144 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load mytags %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'nav_cat_bar.html' %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<h5> 项目设置 </h5>
|
||||||
|
<div class="ibox-tools">
|
||||||
|
<a class="collapse-link">
|
||||||
|
<i class="fa fa-chevron-up"></i>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
</a>
|
||||||
|
<a class="close-link">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox-content">
|
||||||
|
<div class="panel blank-panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li id="tab1" class="active"><a data-toggle="tab" href="#tab-default" aria-expanded="true">默认设置</a></li>
|
||||||
|
{# <li id="tab2" class=""><a data-toggle="tab" href="#tab-email" aria-expanded="true">邮箱设置</a></li>#}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div id="tab-default" class="tab-pane active">
|
||||||
|
<form method="post" id="settingForm" class="form-horizontal" action="">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-warning text-center">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if msg %}
|
||||||
|
<div class="alert alert-success text-center">{{ msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" class="col-sm-2 control-label">默认管理用户<span class="red-fonts">*</span></label>
|
||||||
|
<input name="setting" value="default" style="display: none">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="username" name="username" placeholder="Username" type="text" value="{{ setting_default.field1 }}" class="form-control">
|
||||||
|
<span class="help-block m-b-none"> 管理账号是服务器存在的root等高权限账号(或拥有NOPASSWD: ALL sudo权限),用来推送新建系统用户</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="key" class="col-sm-2 control-label">默认密码</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="password" name="password" placeholder="Password" type="password" class="form-control">
|
||||||
|
<span class="help-block m-b-none">如果不修改密码,请留空</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="port" class="col-sm-2 control-label">默认ssh端口<span class="red-fonts">*</span></label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input id="port" name="port" placeholder="Port" type="text" value="{{ setting_default.field2 }}" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="key" class="col-sm-2 control-label">默认密钥</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<textarea class="form-control" name="key" placeholder="请复制粘贴私钥" rows="10" style="font-size: 9px;"></textarea>
|
||||||
|
<span class="help-block m-b-none">如果不修改密钥,请留空, 密钥密码必填一项</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-white" type="reset">取消</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# <div id="tab-email" class="tab-pane">#}
|
||||||
|
{# <table class="table table-striped table-bordered table-hover " id="editable" >#}
|
||||||
|
{# <thead>#}
|
||||||
|
{# <tr>#}
|
||||||
|
{# <th class="text-center">组名</th>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# </thead>#}
|
||||||
|
{# </table>#}
|
||||||
|
{# </div>#}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block self_footer_js %}
|
||||||
|
<script>
|
||||||
|
$('#settingForm').validator({
|
||||||
|
timely: 2,
|
||||||
|
theme: "yellow_right_effect",
|
||||||
|
rules: {
|
||||||
|
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
||||||
|
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||||
|
either: function(){
|
||||||
|
return $('#password').val() == ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
"username": {
|
||||||
|
rule: "required;check_name",
|
||||||
|
tip: "输入管理用户名",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "用户名称必填"}
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
rule: "required;check_port",
|
||||||
|
tip: "输入端口号",
|
||||||
|
ok: "",
|
||||||
|
msg: {required: "端口号必填"}
|
||||||
|
}
|
||||||
|
{# "key": {#}
|
||||||
|
{# rule: "required(either)",#}
|
||||||
|
{# tip: "输入密钥",#}
|
||||||
|
{# ok: "",#}
|
||||||
|
{# msg: {required: "密码和密钥必填一个!"}#}
|
||||||
|
{# }#}
|
||||||
|
},
|
||||||
|
valid: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|