mirror of https://github.com/jumpserver/jumpserver
merge update...
commit
012950cb68
|
@ -1,7 +1,8 @@
|
|||
*.py[cod]
|
||||
.idea
|
||||
test.py
|
||||
|
||||
.DS_Store
|
||||
db.sqlite3
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
|
|
481
connect.py
481
connect.py
|
@ -8,19 +8,33 @@ sys.setdefaultencoding('utf8')
|
|||
import os
|
||||
import re
|
||||
import time
|
||||
import datetime
|
||||
import textwrap
|
||||
import getpass
|
||||
import readline
|
||||
import django
|
||||
from multiprocessing import Pool
|
||||
import paramiko
|
||||
import struct, fcntl, signal, socket, select, fnmatch
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
|
||||
if django.get_version() != '1.6':
|
||||
django.setup()
|
||||
from jumpserver.api import ServerError, User, Asset, Jtty, get_object
|
||||
from jumpserver.api import logger
|
||||
from jumpserver.api import BisGroup as AssetGroup
|
||||
from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object
|
||||
from jumpserver.api import logger, is_dir, Log, TtyLog
|
||||
from jumpserver.settings import log_dir
|
||||
|
||||
try:
|
||||
import termios
|
||||
import tty
|
||||
except ImportError:
|
||||
print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m'
|
||||
time.sleep(3)
|
||||
sys.exit()
|
||||
|
||||
VIM_FLAG = False
|
||||
VIM_COMMAND = ''
|
||||
SSH_TTY = ''
|
||||
login_user = get_object(User, username=getpass.getuser())
|
||||
|
||||
|
||||
|
@ -82,6 +96,397 @@ def verify_connect(user, option):
|
|||
jtty.connect()
|
||||
|
||||
|
||||
def check_vim_status(command, ssh):
|
||||
global SSH_TTY
|
||||
print command
|
||||
if command == '':
|
||||
return True
|
||||
else:
|
||||
command_str= 'ps -ef |grep "%s" | grep "%s"|grep -v grep |wc -l' % (command,SSH_TTY)
|
||||
print command_str
|
||||
stdin, stdout, stderr = ssh.exec_command(command_str)
|
||||
ps_num = stdout.read()
|
||||
print ps_num
|
||||
if int(ps_num) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def deal_command(str_r, ssh):
|
||||
|
||||
"""
|
||||
处理命令中特殊字符
|
||||
"""
|
||||
str_r = re.sub('\x07','',str_r) #删除响铃
|
||||
patch_char = re.compile('\x08\x1b\[C') #删除方向左右一起的按键
|
||||
while patch_char.search(str_r):
|
||||
str_r = patch_char.sub('', str_r.rstrip())
|
||||
|
||||
result_command = '' #最后的结果
|
||||
pattern_str = '' #模式中间中的字符串
|
||||
backspace_num = 0 #光标移动的个数
|
||||
reach_backspace_flag = False #没有检测到光标键则为true
|
||||
end_flag = False
|
||||
while str_r:
|
||||
tmp = re.match(r'\w', str_r)
|
||||
if tmp:
|
||||
if reach_backspace_flag:
|
||||
pattern_str += str(tmp.group(0))
|
||||
str_r = str_r[1:]
|
||||
continue
|
||||
else:
|
||||
result_command += str(tmp.group(0))
|
||||
str_r = str_r[1:]
|
||||
continue
|
||||
|
||||
tmp = re.match(r'\x1b\[K[\x08]*', str_r)
|
||||
if tmp:
|
||||
if backspace_num > 0:
|
||||
if backspace_num > len(result_command) :
|
||||
result_command += pattern_str
|
||||
result_command = result_command[0:-backspace_num]
|
||||
else:
|
||||
result_command = result_command[0:-backspace_num]
|
||||
result_command += pattern_str
|
||||
del_len = len(str(tmp.group(0)))-3
|
||||
if del_len > 0:
|
||||
result_command = result_command[0:-del_len]
|
||||
reach_backspace_flag = False
|
||||
backspace_num =0
|
||||
pattern_str=''
|
||||
str_r = str_r[len(str(tmp.group(0))):]
|
||||
continue
|
||||
if re.match(r'\x08', str_r):
|
||||
backspace_num += 1
|
||||
reach_backspace_flag = True
|
||||
str_r = str_r[1:]
|
||||
if len(str_r) == 0:
|
||||
end_flag = True
|
||||
continue
|
||||
if reach_backspace_flag :
|
||||
pattern_str += str_r[0]
|
||||
else :
|
||||
result_command += str_r[0]
|
||||
str_r = str_r[1:]
|
||||
|
||||
if backspace_num > 0 and not end_flag:
|
||||
result_command = result_command[:-backspace_num]
|
||||
result_command += pattern_str
|
||||
|
||||
|
||||
|
||||
control_char = re.compile(r"""
|
||||
\x1b[ #%()*+\-.\/]. |
|
||||
\r | #匹配 回车符(CR)
|
||||
(?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd
|
||||
(?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL)
|
||||
(?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST)
|
||||
\x1b. #匹配 转义过后的字符
|
||||
[\x80-\x9f] #匹配 所有控制字符
|
||||
""", re.X)
|
||||
result_command = control_char.sub('', result_command.strip())
|
||||
global VIM_FLAG
|
||||
global VIM_COMMAND
|
||||
if not VIM_FLAG:
|
||||
if result_command.startswith('vim') or result_command.startswith('vi') :
|
||||
VIM_FLAG = True
|
||||
VIM_COMMAND = result_command
|
||||
return result_command.decode('utf8',"ignore")
|
||||
else:
|
||||
if check_vim_status(VIM_COMMAND, ssh):
|
||||
VIM_FLAG = False
|
||||
VIM_COMMAND=''
|
||||
return result_command.decode('utf8',"ignore")
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def newline_code_in(strings):
|
||||
for i in ['\r', '\r\n', '\n']:
|
||||
if i in strings:
|
||||
#print "new line"
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Tty(object):
|
||||
"""
|
||||
A virtual tty class
|
||||
一个虚拟终端类,实现连接ssh和记录日志,基类
|
||||
"""
|
||||
def __init__(self, username, asset_name):
|
||||
self.username = username
|
||||
self.asset_name = asset_name
|
||||
self.ip = None
|
||||
self.port = 22
|
||||
self.channel = None
|
||||
self.user = None
|
||||
self.asset = None
|
||||
self.role = None
|
||||
self.ssh = None
|
||||
self.connect_info = None
|
||||
self.login_type = 'ssh'
|
||||
|
||||
@staticmethod
|
||||
def is_output(strings):
|
||||
newline_char = ['\n', '\r', '\r\n']
|
||||
for char in newline_char:
|
||||
if char in strings:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def remove_control_char(str_r):
|
||||
"""
|
||||
处理日志特殊字符
|
||||
"""
|
||||
control_char = re.compile(r"""
|
||||
\x1b[ #%()*+\-.\/]. |
|
||||
\r | #匹配 回车符(CR)
|
||||
(?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd
|
||||
(?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL)
|
||||
(?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST)
|
||||
\x1b. #匹配 转义过后的字符
|
||||
[\x80-\x9f] #匹配 所有控制字符
|
||||
""", re.X)
|
||||
backspace = re.compile(r"[^\b][\b]")
|
||||
line_filtered = control_char.sub('', str_r.rstrip())
|
||||
while backspace.search(line_filtered):
|
||||
line_filtered = backspace.sub('', line_filtered)
|
||||
|
||||
return line_filtered
|
||||
|
||||
def get_log_file(self):
|
||||
"""
|
||||
Logging user command and output.
|
||||
记录用户的日志
|
||||
"""
|
||||
tty_log_dir = os.path.join(log_dir, 'tty')
|
||||
timestamp_start = int(time.time())
|
||||
date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start))
|
||||
time_start = time.strftime('%H%M%S', time.localtime(timestamp_start))
|
||||
today_connect_log_dir = os.path.join(tty_log_dir, date_start)
|
||||
log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.asset_name, time_start))
|
||||
|
||||
try:
|
||||
is_dir(today_connect_log_dir, mode=0777)
|
||||
except OSError:
|
||||
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
|
||||
|
||||
try:
|
||||
log_file_f = open(log_file_path + '.log', 'a')
|
||||
log_time_f = open(log_file_path + '.time', 'a')
|
||||
except IOError:
|
||||
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
|
||||
|
||||
if self.login_type == 'ssh':
|
||||
pid = os.getpid()
|
||||
remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n')
|
||||
log = Log(user=self.username, host=self.asset_name, remote_ip=remote_ip,
|
||||
log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid)
|
||||
else:
|
||||
remote_ip = 'Web'
|
||||
log = Log(user=self.username, host=self.asset_name, remote_ip=remote_ip,
|
||||
log_path=log_file_path, start_time=datetime.datetime.now(), pid=0)
|
||||
log.save()
|
||||
log.pid = log.id
|
||||
log.save()
|
||||
|
||||
log_file_f.write('Start at %s\n' % datetime.datetime.now())
|
||||
log.save()
|
||||
return log_file_f, log_time_f, log
|
||||
|
||||
def get_connect_info(self):
|
||||
"""
|
||||
获取需要登陆的主机的信息和映射用户的账号密码
|
||||
"""
|
||||
|
||||
# 1. get ip, port
|
||||
# 2. get 映射用户
|
||||
# 3. get 映射用户的账号,密码或者key
|
||||
# self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''}
|
||||
self.connect_info = {'user': 'a', 'asset': 'b', 'ip': '127.0.0.1', 'port': 22, 'role_name': 'root', 'role_pass': '', 'role_key': '/root/.ssh/id_rsa.bak'}
|
||||
return self.connect_info
|
||||
|
||||
def get_connection(self):
|
||||
"""
|
||||
获取连接成功后的ssh
|
||||
"""
|
||||
connect_info = self.get_connect_info()
|
||||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
if connect_info.get('role_pass'):
|
||||
ssh.connect(connect_info.get('ip'),
|
||||
port=connect_info.get('port'),
|
||||
username=connect_info.get('role_name'),
|
||||
password=connect_info.get('role_pass'),
|
||||
look_for_keys=False)
|
||||
else:
|
||||
ssh.connect(connect_info.get('ip'),
|
||||
port=connect_info.get('port'),
|
||||
username=connect_info.get('role_name'),
|
||||
key_filename=connect_info.get('role_key'),
|
||||
look_for_keys=False)
|
||||
|
||||
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
|
||||
raise ServerError('认证失败 Authentication Error.')
|
||||
except socket.error:
|
||||
raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.')
|
||||
else:
|
||||
self.ssh = ssh
|
||||
return ssh
|
||||
|
||||
|
||||
class SshTty(Tty):
|
||||
"""
|
||||
A virtual tty class
|
||||
一个虚拟终端类,实现连接ssh和记录日志
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_win_size():
|
||||
"""
|
||||
This function use to get the size of the windows!
|
||||
获得terminal窗口大小
|
||||
"""
|
||||
if 'TIOCGWINSZ' in dir(termios):
|
||||
TIOCGWINSZ = termios.TIOCGWINSZ
|
||||
else:
|
||||
TIOCGWINSZ = 1074295912L
|
||||
s = struct.pack('HHHH', 0, 0, 0, 0)
|
||||
x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
|
||||
return struct.unpack('HHHH', x)[0:2]
|
||||
|
||||
def set_win_size(self, sig, data):
|
||||
"""
|
||||
This function use to set the window size of the terminal!
|
||||
设置terminal窗口大小
|
||||
"""
|
||||
try:
|
||||
win_size = self.get_win_size()
|
||||
self.channel.resize_pty(height=win_size[0], width=win_size[1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def posix_shell(self):
|
||||
"""
|
||||
Use paramiko channel connect server interactive.
|
||||
使用paramiko模块的channel,连接后端,进入交互式
|
||||
"""
|
||||
log_file_f, log_time_f, log = self.get_log_file()
|
||||
old_tty = termios.tcgetattr(sys.stdin)
|
||||
pre_timestamp = time.time()
|
||||
input_r = ''
|
||||
input_mode = False
|
||||
|
||||
try:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
tty.setcbreak(sys.stdin.fileno())
|
||||
self.channel.settimeout(0.0)
|
||||
|
||||
while True:
|
||||
try:
|
||||
r, w, e = select.select([self.channel, sys.stdin], [], [])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.channel in r:
|
||||
try:
|
||||
x = self.channel.recv(1024)
|
||||
if len(x) == 0:
|
||||
break
|
||||
sys.stdout.write(x)
|
||||
sys.stdout.flush()
|
||||
now_timestamp = time.time()
|
||||
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
|
||||
log_file_f.write(x)
|
||||
pre_timestamp = now_timestamp
|
||||
log_file_f.flush()
|
||||
log_time_f.flush()
|
||||
|
||||
if input_mode and not self.is_output(x):
|
||||
input_r += x
|
||||
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
if sys.stdin in r:
|
||||
x = os.read(sys.stdin.fileno(), 1)
|
||||
if not input_mode:
|
||||
input_mode = True
|
||||
|
||||
if str(x) in ['\r', '\n', '\r\n']:
|
||||
# input_r = deal_command(input_r,ssh)
|
||||
input_r = self.remove_control_char(input_r)
|
||||
|
||||
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=input_r).save()
|
||||
input_r = ''
|
||||
input_mode = False
|
||||
|
||||
if len(x) == 0:
|
||||
break
|
||||
self.channel.send(x)
|
||||
|
||||
finally:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
|
||||
log_file_f.write('End time is %s' % datetime.datetime.now())
|
||||
log_file_f.close()
|
||||
log.is_finished = True
|
||||
log.end_time = datetime.datetime.now()
|
||||
log.save()
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Connect server.
|
||||
连接服务器
|
||||
"""
|
||||
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
|
||||
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip
|
||||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = self.get_connection()
|
||||
|
||||
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
|
||||
global channel
|
||||
win_size = self.get_win_size()
|
||||
self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
|
||||
try:
|
||||
signal.signal(signal.SIGWINCH, self.set_win_size)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 设置PS1并提示 Set PS1 and msg it
|
||||
#channel.send(ps1)
|
||||
#channel.send(login_msg)
|
||||
# channel.send('echo ${SSH_TTY}\n')
|
||||
# global SSH_TTY
|
||||
# while not channel.recv_ready():
|
||||
# time.sleep(1)
|
||||
# tmp = channel.recv(1024)
|
||||
#print 'ok'+tmp+'ok'
|
||||
# SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip()
|
||||
# SSH_TTY = ''
|
||||
channel.send('clear\n')
|
||||
# Make ssh interactive tunnel
|
||||
self.posix_shell()
|
||||
|
||||
# Shutdown channel socket
|
||||
channel.close()
|
||||
ssh.close()
|
||||
|
||||
def execute(self, cmd):
|
||||
"""
|
||||
execute cmd on the asset
|
||||
执行命令
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def print_prompt():
|
||||
"""
|
||||
Print prompt
|
||||
|
@ -98,76 +503,6 @@ def print_prompt():
|
|||
print textwrap.dedent(msg)
|
||||
|
||||
|
||||
# def remote_exec_cmd(ip, port, username, password, cmd):
|
||||
# try:
|
||||
# time.sleep(5)
|
||||
# ssh = paramiko.SSHClient()
|
||||
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
# ssh.connect(ip, port, username, password, timeout=5)
|
||||
# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd)
|
||||
# out = stdout.readlines()
|
||||
# err = stderr.readlines()
|
||||
# color_print('%s:' % ip, 'blue')
|
||||
# for i in out:
|
||||
# color_print(" " * 4 + i.strip(), 'green')
|
||||
# for j in err:
|
||||
# color_print(" " * 4 + j.strip(), 'red')
|
||||
# ssh.close()
|
||||
# except Exception as e:
|
||||
# color_print(ip + ':', 'blue')
|
||||
# color_print(str(e), 'red')
|
||||
|
||||
|
||||
# def multi_remote_exec_cmd(hosts, username, cmd):
|
||||
# pool = Pool(processes=5)
|
||||
# for host in hosts:
|
||||
# username, password, ip, port = get_connect_item(username, host)
|
||||
# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd))
|
||||
# pool.close()
|
||||
# pool.join()
|
||||
|
||||
|
||||
# def exec_cmd_servers(username):
|
||||
# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green')
|
||||
# user.get_asset_info(printable=True)
|
||||
# while True:
|
||||
# hosts = []
|
||||
# inputs = raw_input('\033[1;32mip(s)>: \033[0m')
|
||||
# if inputs in ['q', 'Q']:
|
||||
# break
|
||||
# get_hosts = login_user.get_asset_info().keys()
|
||||
#
|
||||
# if ',' in inputs:
|
||||
# ips_input = inputs.split(',')
|
||||
# for host in ips_input:
|
||||
# if host in get_hosts:
|
||||
# hosts.append(host)
|
||||
# else:
|
||||
# for host in get_hosts:
|
||||
# if fnmatch.fnmatch(host, inputs):
|
||||
# hosts.append(host.strip())
|
||||
#
|
||||
# if len(hosts) == 0:
|
||||
# color_print("Check again, Not matched any ip!", 'red')
|
||||
# continue
|
||||
# else:
|
||||
# print "You matched ip: %s" % hosts
|
||||
# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green')
|
||||
# while True:
|
||||
# cmd = raw_input('\033[1;32mCmd(s): \033[0m')
|
||||
# if cmd in ['q', 'Q']:
|
||||
# break
|
||||
# exec_log_dir = os.path.join(log_dir, 'exec_cmds')
|
||||
# if not os.path.isdir(exec_log_dir):
|
||||
# os.mkdir(exec_log_dir)
|
||||
# os.chmod(exec_log_dir, 0777)
|
||||
# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d'))
|
||||
# f = open(filename, 'a')
|
||||
# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" %
|
||||
# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd))
|
||||
# multi_remote_exec_cmd(hosts, username, cmd)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
he he
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
- 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
|
||||
- model: juser.user
|
||||
pk: 5001
|
||||
fields:
|
||||
username: group_admin
|
||||
name: group_admin
|
||||
password: pbkdf2_sha256$20000$ttObUWd15q10$NJoyZf2OZz9oiw2g4j2TkTh9zGgyVDRFdUkhn8X0nB0=
|
||||
email: group_admin@jumpserver.org
|
||||
role: GA
|
||||
is_active: 1
|
||||
- model: juser.usergroup
|
||||
pk: 1
|
||||
fields:
|
||||
name: ALL
|
||||
comment: ALL
|
||||
- model: juser.usergroup
|
||||
pk: 2
|
||||
fields:
|
||||
name: 默认
|
||||
comment: 默认
|
|
@ -60,12 +60,12 @@ class AssetGroup(models.Model):
|
|||
|
||||
class Asset(models.Model):
|
||||
ip = models.GenericIPAddressField(unique=True)
|
||||
port = models.IntegerField(blank=True, null=True)
|
||||
port = models.IntegerField()
|
||||
group = models.ManyToManyField(AssetGroup)
|
||||
username = models.CharField(max_length=20, blank=True, null=True)
|
||||
password = models.CharField(max_length=80, blank=True, null=True)
|
||||
use_default = models.BooleanField(default=True)
|
||||
date_added = models.DateTimeField(auto_now=True)
|
||||
use_default_auth = models.BooleanField(default=True)
|
||||
date_added = models.DateTimeField(auto_now_add=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
comment = models.CharField(max_length=100, blank=True, null=True)
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# coding: utf-8
|
||||
|
||||
|
||||
from argparse import ArgumentParser, FileType
|
||||
from contextlib import closing
|
||||
from codecs import open as copen
|
||||
from json import dumps
|
||||
from math import ceil
|
||||
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')
|
||||
|
||||
|
||||
def escapeString(string):
|
||||
string = string.encode('unicode_escape').decode('utf-8')
|
||||
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:
|
||||
data = escapeString(scriptf.read(t[1]))
|
||||
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') 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
|
||||
|
||||
|
|
@ -5,12 +5,10 @@ class Log(models.Model):
|
|||
user = models.CharField(max_length=20, null=True)
|
||||
host = models.CharField(max_length=20, null=True)
|
||||
remote_ip = models.CharField(max_length=100)
|
||||
dept_name = models.CharField(max_length=20)
|
||||
log_path = models.CharField(max_length=100)
|
||||
start_time = models.DateTimeField(null=True)
|
||||
pid = models.IntegerField()
|
||||
is_finished = models.BooleanField(default=False)
|
||||
handle_finished = models.BooleanField(default=False)
|
||||
end_time = models.DateTimeField(null=True)
|
||||
|
||||
def __unicode__(self):
|
||||
|
@ -20,4 +18,11 @@ class Log(models.Model):
|
|||
class Alert(models.Model):
|
||||
msg = models.CharField(max_length=20)
|
||||
time = models.DateTimeField(null=True)
|
||||
is_finished = models.BigIntegerField(default=False)
|
||||
is_finished = models.BigIntegerField(default=False)
|
||||
|
||||
|
||||
class TtyLog(models.Model):
|
||||
log = models.ForeignKey(Log)
|
||||
datetime = models.DateTimeField()
|
||||
cmd = models.CharField(max_length=200)
|
||||
|
||||
|
|
13
jlog/urls.py
13
jlog/urls.py
|
@ -3,9 +3,10 @@ from django.conf.urls import patterns, include, url
|
|||
from jlog.views import *
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', log_list),
|
||||
url(r'^log_list/(\w+)/$', log_list),
|
||||
url(r'^log_kill/', log_kill),
|
||||
url(r'^history/$', log_history),
|
||||
url(r'^search/$', log_search),
|
||||
)
|
||||
url(r'^$', log_list),
|
||||
url(r'^log_list/(\w+)/$', log_list),
|
||||
url(r'^history/$', log_history),
|
||||
url(r'^log_kill/', log_kill),
|
||||
url(r'^record/$', log_record),
|
||||
url(r'^web_terminal/$', web_terminal),
|
||||
)
|
147
jlog/views.py
147
jlog/views.py
|
@ -4,78 +4,62 @@ from django.template import RequestContext
|
|||
from django.shortcuts import render_to_response
|
||||
|
||||
from jumpserver.api import *
|
||||
from jasset.views import httperror
|
||||
from django.http import HttpResponseNotFound
|
||||
from jlog.log_api import renderTemplate
|
||||
|
||||
CONF = ConfigParser()
|
||||
CONF.read('%s/jumpserver.conf' % BASE_DIR)
|
||||
from models import Log
|
||||
from jumpserver.settings import web_socket_host
|
||||
|
||||
|
||||
def get_user_info(request, offset):
|
||||
""" 获取用户信息及环境 """
|
||||
env_dic = {'online': 0, 'offline': 1}
|
||||
env = env_dic[offset]
|
||||
keyword = request.GET.get('keyword', '')
|
||||
user_info = get_session_user_info(request)
|
||||
user_id, username = user_info[0:2]
|
||||
dept_id, dept_name = user_info[3:5]
|
||||
ret = [request, keyword, env, username, dept_name]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_user_log(ret_list):
|
||||
""" 获取不同类型用户日志记录 """
|
||||
request, keyword, env, username, dept_name = ret_list
|
||||
post_all = Log.objects.filter(is_finished=env).order_by('-start_time')
|
||||
post_keyword_all = Log.objects.filter(Q(user__contains=keyword) |
|
||||
Q(host__contains=keyword)) \
|
||||
.filter(is_finished=env).order_by('-start_time')
|
||||
|
||||
if is_super_user(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all
|
||||
else:
|
||||
posts = post_all
|
||||
|
||||
elif is_group_admin(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all.filter(dept_name=dept_name)
|
||||
else:
|
||||
posts = post_all.filter(dept_name=dept_name)
|
||||
|
||||
elif is_common_user(request):
|
||||
if keyword:
|
||||
posts = post_keyword_all.filter(user=username)
|
||||
else:
|
||||
posts = post_all.filter(user=username)
|
||||
|
||||
return posts
|
||||
|
||||
|
||||
@require_login
|
||||
@require_role('admin')
|
||||
def log_list(request, offset):
|
||||
""" 显示日志 """
|
||||
header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户'
|
||||
keyword = request.GET.get('keyword', '')
|
||||
web_socket_host = CONF.get('websocket', 'web_socket_host')
|
||||
posts = get_user_log(get_user_info(request, offset))
|
||||
header_title, path1 = u'审计', u'操作审计'
|
||||
date_seven_day = request.GET.get('start', '')
|
||||
date_now_str = request.GET.get('end', '')
|
||||
username_list = request.GET.getlist('username', [])
|
||||
host_list = request.GET.getlist('host', [])
|
||||
cmd = request.GET.get('cmd', '')
|
||||
print date_seven_day, date_now_str
|
||||
if offset == 'online':
|
||||
posts = Log.objects.filter(is_finished=False).order_by('-start_time')
|
||||
else:
|
||||
posts = Log.objects.filter(is_finished=True).order_by('-start_time')
|
||||
username_all = set([log.user for log in Log.objects.all()])
|
||||
ip_all = set([log.host for log in Log.objects.all()])
|
||||
|
||||
if date_seven_day and date_now_str:
|
||||
datetime_start = datetime.datetime.strptime(date_seven_day + ' 00:00:01', '%m/%d/%Y %H:%M:%S')
|
||||
datetime_end = datetime.datetime.strptime(date_now_str + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
|
||||
posts = posts.filter(start_time__gte=datetime_start).filter(start_time__lte=datetime_end)
|
||||
|
||||
if username_list:
|
||||
posts = posts.filter(user__in=username_list)
|
||||
|
||||
if host_list:
|
||||
posts = posts.filter(host__in=host_list)
|
||||
if cmd:
|
||||
log_id_list = set([log.log_id for log in TtyLog.objects.filter(cmd__contains=cmd)])
|
||||
posts = posts.filter(id__in=log_id_list)
|
||||
else:
|
||||
date_now = datetime.datetime.now()
|
||||
date_now_str = date_now.strftime('%m/%d/%Y')
|
||||
date_seven_day = (date_now + datetime.timedelta(days=-7)).strftime('%m/%d/%Y')
|
||||
|
||||
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
|
||||
web_monitor_uri = 'ws://%s/monitor' % web_socket_host
|
||||
web_kill_uri = 'http://%s/kill' % web_socket_host
|
||||
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@require_admin
|
||||
@require_role('admin')
|
||||
def log_kill(request):
|
||||
""" 杀掉connect进程 """
|
||||
pid = request.GET.get('id', '')
|
||||
log = Log.objects.filter(pid=pid)
|
||||
if log:
|
||||
log = log[0]
|
||||
dept_name = log.dept_name
|
||||
deptname = get_session_user_info(request)[4]
|
||||
if is_group_admin(request) and dept_name != deptname:
|
||||
return httperror(request, u'Kill失败, 您无权操作!')
|
||||
try:
|
||||
os.kill(int(pid), 9)
|
||||
except OSError:
|
||||
|
@ -86,35 +70,44 @@ def log_kill(request):
|
|||
return HttpResponseNotFound(u'没有此进程!')
|
||||
|
||||
|
||||
@require_login
|
||||
@require_role('admin')
|
||||
def log_history(request):
|
||||
""" 命令历史记录 """
|
||||
log_id = request.GET.get('id', 0)
|
||||
log = Log.objects.filter(id=log_id)
|
||||
if log:
|
||||
log = log[0]
|
||||
tty_logs = log.ttylog_set.all()
|
||||
|
||||
if tty_logs:
|
||||
content = ''
|
||||
for tty_log in tty_logs:
|
||||
content += '%s: %s\n' % (tty_log.datetime.strftime('%Y-%m-%d %H:%M:%S'), tty_log.cmd)
|
||||
return HttpResponse(content)
|
||||
|
||||
return HttpResponse('无日志记录!')
|
||||
|
||||
|
||||
@require_role('admin')
|
||||
def log_record(request):
|
||||
log_id = request.GET.get('id', 0)
|
||||
log = Log.objects.filter(id=int(log_id))
|
||||
if log:
|
||||
log = log[0]
|
||||
dept_name = log.dept_name
|
||||
deptname = get_session_user_info(request)[4]
|
||||
if is_group_admin(request) and dept_name != deptname:
|
||||
return httperror(request, '查看失败, 您无权查看!')
|
||||
|
||||
elif is_common_user(request):
|
||||
return httperror(request, '查看失败, 您无权查看!')
|
||||
|
||||
log_his = "%s.his" % log.log_path
|
||||
if os.path.isfile(log_his):
|
||||
f = open(log_his)
|
||||
content = f.read()
|
||||
log_file = log.log_path + '.log'
|
||||
log_time = log.log_path + '.time'
|
||||
if os.path.isfile(log_file) and os.path.isfile(log_time):
|
||||
content = renderTemplate(log_file, log_time)
|
||||
return HttpResponse(content)
|
||||
else:
|
||||
return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!')
|
||||
return HttpResponse('无日志记录!')
|
||||
|
||||
|
||||
@require_login
|
||||
def log_search(request):
|
||||
""" 日志搜索 """
|
||||
offset = request.GET.get('env', '')
|
||||
keyword = request.GET.get('keyword', '')
|
||||
posts = get_user_log(get_user_info(request, offset))
|
||||
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
|
||||
return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request))
|
||||
def web_terminal(request):
|
||||
#username = get_session.get('username', '')
|
||||
token = request.COOKIES.get('sessionid')
|
||||
username = request.user.username
|
||||
asset_name = '127.0.0.1'
|
||||
web_terminal_uri = 'ws://%s/terminal?username=%s&asset_name=%s&token=%s' % (web_socket_host, username, asset_name, token)
|
||||
return render_to_response('jlog/web_terminal.html', locals())
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ root_dn = cn=admin,dc=jumpserver,dc=org
|
|||
root_pw = secret234
|
||||
|
||||
[websocket]
|
||||
web_socket_host = 1127.0.0.1:3000
|
||||
web_socket_host = 127.0.0.1:3000
|
||||
|
||||
|
||||
|
||||
[mail]
|
||||
mail_enable = 1
|
||||
|
@ -28,4 +30,4 @@ email_host = smtp.exmail.qq.com
|
|||
email_port = 25
|
||||
email_host_user = noreply@jumpserver.org
|
||||
email_host_password = jumpserver1234
|
||||
email_use_tls = False
|
||||
email_use_tls = True
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os, sys, time
|
||||
from ConfigParser import ConfigParser
|
||||
import getpass
|
||||
import os, sys, time, re
|
||||
from Crypto.Cipher import AES
|
||||
import crypt
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
|
@ -10,16 +8,15 @@ import hashlib
|
|||
import datetime
|
||||
import random
|
||||
import subprocess
|
||||
import paramiko
|
||||
import struct, fcntl, signal,socket, select, fnmatch
|
||||
from settings import *
|
||||
|
||||
from django.core.paginator import Paginator, EmptyPage, InvalidPage
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.template import RequestContext
|
||||
from juser.models import User, UserGroup
|
||||
from jasset.models import Asset, AssetGroup
|
||||
from jasset.models import AssetAlias
|
||||
from jlog.models import Log
|
||||
# from jlog.models import Log
|
||||
from jlog.models import Log, TtyLog
|
||||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response
|
||||
|
@ -27,29 +24,6 @@ from django.core.mail import send_mail
|
|||
import json
|
||||
import logging
|
||||
|
||||
try:
|
||||
import termios
|
||||
import tty
|
||||
except ImportError:
|
||||
print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m'
|
||||
time.sleep(3)
|
||||
sys.exit()
|
||||
|
||||
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
CONF = ConfigParser()
|
||||
CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf'))
|
||||
LOG_DIR = os.path.join(BASE_DIR, 'logs')
|
||||
JLOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log')
|
||||
SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys')
|
||||
# SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server')
|
||||
KEY = CONF.get('base', 'key')
|
||||
LOGIN_NAME = getpass.getuser()
|
||||
# LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable')
|
||||
URL = CONF.get('base', 'url')
|
||||
MAIL_ENABLE = CONF.get('mail', 'mail_enable')
|
||||
MAIL_FROM = CONF.get('mail', 'email_host_user')
|
||||
log_dir = os.path.join(BASE_DIR, 'logs')
|
||||
|
||||
|
||||
def set_log(level):
|
||||
|
@ -69,75 +43,6 @@ def set_log(level):
|
|||
return logger_f
|
||||
|
||||
|
||||
# class LDAPMgmt():
|
||||
# """
|
||||
# LDAP class for add, select, del, update
|
||||
# LDAP 管理类,增删改查
|
||||
# """
|
||||
# def __init__(self,
|
||||
# host_url,
|
||||
# base_dn,
|
||||
# root_cn,
|
||||
# root_pw):
|
||||
# self.ldap_host = host_url
|
||||
# self.ldap_base_dn = base_dn
|
||||
# self.conn = ldap.initialize(host_url)
|
||||
# self.conn.set_option(ldap.OPT_REFERRALS, 0)
|
||||
# self.conn.protocol_version = ldap.VERSION3
|
||||
# self.conn.simple_bind_s(root_cn, root_pw)
|
||||
#
|
||||
# def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None):
|
||||
# """
|
||||
# query
|
||||
# 查询
|
||||
# """
|
||||
# result = {}
|
||||
# try:
|
||||
# ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr)
|
||||
# for entry in ldap_result:
|
||||
# name, data = entry
|
||||
# for k, v in data.items():
|
||||
# print '%s: %s' % (k, v)
|
||||
# result[k] = v
|
||||
# return result
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def add(self, dn, attrs):
|
||||
# """
|
||||
# add
|
||||
# 添加
|
||||
# """
|
||||
# try:
|
||||
# ldif = modlist.addModlist(attrs)
|
||||
# self.conn.add_s(dn, ldif)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def modify(self, dn, attrs):
|
||||
# """
|
||||
# modify
|
||||
# 更改
|
||||
# """
|
||||
# try:
|
||||
# attr_s = []
|
||||
# for k, v in attrs.items():
|
||||
# attr_s.append((2, k, v))
|
||||
# self.conn.modify_s(dn, attr_s)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
#
|
||||
# def delete(self, dn):
|
||||
# """
|
||||
# delete
|
||||
# 删除
|
||||
# """
|
||||
# try:
|
||||
# self.conn.delete_s(dn)
|
||||
# except ldap.LDAPError, e:
|
||||
# print e
|
||||
|
||||
|
||||
def page_list_return(total, current=1):
|
||||
"""
|
||||
page
|
||||
|
@ -146,7 +51,7 @@ def page_list_return(total, current=1):
|
|||
min_page = current - 2 if current - 4 > 0 else 1
|
||||
max_page = min_page + 4 if min_page + 4 < total else total
|
||||
|
||||
return range(min_page, max_page+1)
|
||||
return range(min_page, max_page + 1)
|
||||
|
||||
|
||||
def pages(post_objects, request):
|
||||
|
@ -181,208 +86,6 @@ def pages(post_objects, request):
|
|||
return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end
|
||||
|
||||
|
||||
class Jtty(object):
|
||||
"""
|
||||
A virtual tty class
|
||||
一个虚拟终端类,实现连接ssh和记录日志
|
||||
"""
|
||||
def __init__(self, user, asset):
|
||||
self.chan = None
|
||||
self.username = user.username
|
||||
self.ip = asset.ip
|
||||
self.user = user
|
||||
self.asset = asset
|
||||
|
||||
@staticmethod
|
||||
def get_win_size():
|
||||
"""
|
||||
This function use to get the size of the windows!
|
||||
获得terminal窗口大小
|
||||
"""
|
||||
if 'TIOCGWINSZ' in dir(termios):
|
||||
TIOCGWINSZ = termios.TIOCGWINSZ
|
||||
else:
|
||||
TIOCGWINSZ = 1074295912L
|
||||
s = struct.pack('HHHH', 0, 0, 0, 0)
|
||||
x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
|
||||
return struct.unpack('HHHH', x)[0:2]
|
||||
|
||||
def set_win_size(self, sig, data):
|
||||
"""
|
||||
This function use to set the window size of the terminal!
|
||||
设置terminal窗口大小
|
||||
"""
|
||||
try:
|
||||
win_size = self.get_win_size()
|
||||
self.chan.resize_pty(height=win_size[0], width=win_size[1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def log_record(self):
|
||||
"""
|
||||
Logging user command and output.
|
||||
记录用户的日志
|
||||
"""
|
||||
tty_log_dir = os.path.join(log_dir, 'tty')
|
||||
timestamp_start = int(time.time())
|
||||
date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start))
|
||||
time_start = time.strftime('%H%M%S', time.localtime(timestamp_start))
|
||||
log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start)
|
||||
today_connect_log_dir = os.path.join(tty_log_dir, date_start)
|
||||
log_file_path = os.path.join(today_connect_log_dir, log_filename)
|
||||
dept_name = self.user.dept.name
|
||||
|
||||
pid = os.getpid()
|
||||
pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip()
|
||||
ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n')
|
||||
|
||||
try:
|
||||
is_dir(today_connect_log_dir)
|
||||
except OSError:
|
||||
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
|
||||
|
||||
try:
|
||||
log_file = open(log_file_path, 'a')
|
||||
except IOError:
|
||||
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
|
||||
|
||||
log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name,
|
||||
log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid)
|
||||
log_file.write('Start time is %s\n' % datetime.datetime.now())
|
||||
log.save()
|
||||
return log_file, log
|
||||
|
||||
def posix_shell(self):
|
||||
"""
|
||||
Use paramiko channel connect server interactive.
|
||||
使用paramiko模块的channel,连接后端,进入交互式
|
||||
"""
|
||||
log_file, log = self.log_record()
|
||||
old_tty = termios.tcgetattr(sys.stdin)
|
||||
try:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
tty.setcbreak(sys.stdin.fileno())
|
||||
self.chan.settimeout(0.0)
|
||||
|
||||
while True:
|
||||
try:
|
||||
r, w, e = select.select([self.chan, sys.stdin], [], [])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.chan in r:
|
||||
try:
|
||||
x = self.chan.recv(1024)
|
||||
if len(x) == 0:
|
||||
break
|
||||
sys.stdout.write(x)
|
||||
sys.stdout.flush()
|
||||
log_file.write(x)
|
||||
log_file.flush()
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
if sys.stdin in r:
|
||||
x = os.read(sys.stdin.fileno(), 1)
|
||||
if len(x) == 0:
|
||||
break
|
||||
self.chan.send(x)
|
||||
|
||||
finally:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
|
||||
log_file.write('End time is %s' % datetime.datetime.now())
|
||||
log_file.close()
|
||||
log.is_finished = True
|
||||
log.handle_finished = False
|
||||
log.end_time = datetime.datetime.now()
|
||||
log.save()
|
||||
|
||||
def get_connect_item(self):
|
||||
"""
|
||||
get args for connect: ip, port, username, passwd
|
||||
获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码
|
||||
"""
|
||||
if not self.asset.is_active:
|
||||
raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
|
||||
|
||||
if not self.user.is_active:
|
||||
raise ServerError('该用户被禁用 User %s is not active.' % self.username)
|
||||
|
||||
login_type_dict = {
|
||||
'L': self.user.ldap_pwd,
|
||||
}
|
||||
|
||||
if self.asset.login_type in login_type_dict:
|
||||
password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type])
|
||||
return self.username, password, self.ip, int(self.asset.port)
|
||||
|
||||
elif self.asset.login_type == 'M':
|
||||
username = self.asset.username
|
||||
password = CRYPTOR.decrypt(self.asset.password)
|
||||
return username, password, self.ip, int(self.asset.port)
|
||||
|
||||
else:
|
||||
raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]')
|
||||
|
||||
def get_connection(self):
|
||||
"""
|
||||
Get the ssh connection for reuse
|
||||
获取连接套接字
|
||||
"""
|
||||
username, password, ip, port = self.get_connect_item()
|
||||
logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port))
|
||||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
ssh.connect(ip, port=port, username=username, password=password, compress=True)
|
||||
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
|
||||
raise ServerError('认证错误 Authentication Error.')
|
||||
except socket.error:
|
||||
raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.')
|
||||
else:
|
||||
return ssh
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Connect server.
|
||||
连接服务器
|
||||
"""
|
||||
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
|
||||
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.asset.ip
|
||||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = self.get_connection()
|
||||
|
||||
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
|
||||
global channel
|
||||
win_size = self.get_win_size()
|
||||
self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1])
|
||||
try:
|
||||
signal.signal(signal.SIGWINCH, self.set_win_size)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 设置PS1并提示 Set PS1 and msg it
|
||||
channel.send(ps1)
|
||||
channel.send(login_msg)
|
||||
|
||||
# Make ssh interactive tunnel
|
||||
self.posix_shell()
|
||||
|
||||
# Shutdown channel socket
|
||||
channel.close()
|
||||
ssh.close()
|
||||
|
||||
def execute(self, cmd):
|
||||
"""
|
||||
execute cmd on the asset
|
||||
执行命令
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class PyCrypt(object):
|
||||
"""
|
||||
|
@ -404,7 +107,7 @@ class PyCrypt(object):
|
|||
symbol = '!@$%^&*()_'
|
||||
salt_list = []
|
||||
if especial:
|
||||
for i in range(length-4):
|
||||
for i in range(length - 4):
|
||||
salt_list.append(random.choice(salt_key))
|
||||
for i in range(4):
|
||||
salt_list.append(random.choice(symbol))
|
||||
|
@ -493,19 +196,24 @@ def require_role(role='user'):
|
|||
decorator for require user role in ["super", "admin", "user"]
|
||||
要求用户是某种角色 ["super", "admin", "user"]的装饰器
|
||||
"""
|
||||
|
||||
def _deco(func):
|
||||
def __deco(request, *args, **kwargs):
|
||||
if role == 'user':
|
||||
if not request.session.get('user_id'):
|
||||
return HttpResponseRedirect('/login/')
|
||||
elif role == 'admin':
|
||||
if request.session.get('role_id', 0) < 1:
|
||||
if not request.user.is_authenticated():
|
||||
return HttpResponseRedirect('/login/')
|
||||
|
||||
if role == 'admin':
|
||||
# if request.session.get('role_id', 0) < 1:
|
||||
if request.user.role == 'CU':
|
||||
return HttpResponseRedirect('/')
|
||||
elif role == 'super':
|
||||
if request.session.get('role_id', 0) < 2:
|
||||
# if request.session.get('role_id', 0) < 2:
|
||||
if request.user.role in ['CU', 'GA']:
|
||||
return HttpResponseRedirect('/')
|
||||
return func(request, *args, **kwargs)
|
||||
|
||||
return __deco
|
||||
|
||||
return _deco
|
||||
|
||||
|
||||
|
@ -514,8 +222,8 @@ def is_role_request(request, role='user'):
|
|||
require this request of user is right
|
||||
要求请求角色正确
|
||||
"""
|
||||
role_all = {'user': 0, 'admin': 1, 'super': 2}
|
||||
if request.session.get('role_id') == role_all.get(role, 0):
|
||||
role_all = {'user': 'CU', 'admin': 'GA', 'super': 'SU'}
|
||||
if request.user.role == role_all.get(role, 'CU'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -526,13 +234,14 @@ def get_session_user_dept(request):
|
|||
get department of the user in session
|
||||
获取session中用户的部门
|
||||
"""
|
||||
user_id = request.session.get('user_id', 0)
|
||||
print '#' * 20
|
||||
print user_id
|
||||
user = User.objects.filter(id=user_id)
|
||||
if user:
|
||||
user = user[0]
|
||||
return user, None
|
||||
# user_id = request.session.get('user_id', 0)
|
||||
# print '#' * 20
|
||||
# print user_id
|
||||
# user = User.objects.filter(id=user_id)
|
||||
# if user:
|
||||
# user = user[0]
|
||||
# return user, None
|
||||
return request.user, None
|
||||
|
||||
|
||||
@require_role
|
||||
|
@ -541,18 +250,18 @@ def get_session_user_info(request):
|
|||
get the user info of the user in session, for example id, username etc.
|
||||
获取用户的信息
|
||||
"""
|
||||
user_id = request.session.get('user_id', 0)
|
||||
user = get_object(User, id=user_id)
|
||||
if user:
|
||||
return [user.id, user.username, user]
|
||||
|
||||
# user_id = request.session.get('user_id', 0)
|
||||
# user = get_object(User, id=user_id)
|
||||
# if user:
|
||||
# return [user.id, user.username, user]
|
||||
return [request.user.id, request.user.username, request.user]
|
||||
|
||||
def get_user_dept(request):
|
||||
"""
|
||||
get the user dept id
|
||||
获取用户的部门id
|
||||
"""
|
||||
user_id = request.session.get('user_id')
|
||||
user_id = request.user.id
|
||||
if user_id:
|
||||
user_dept = User.objects.get(id=user_id).dept
|
||||
return user_dept.id
|
||||
|
@ -588,7 +297,7 @@ def validate(request, user_group=None, user=None, asset_group=None, asset=None,
|
|||
if edept:
|
||||
if dept.id != int(edept[0]):
|
||||
return False
|
||||
|
||||
|
||||
if user_group:
|
||||
dept_user_groups = dept.usergroup_set.all()
|
||||
user_group_ids = []
|
||||
|
@ -705,15 +414,5 @@ def my_render(template, data, request):
|
|||
|
||||
|
||||
CRYPTOR = PyCrypt(KEY)
|
||||
logger = set_log(log_level)
|
||||
|
||||
# if LDAP_ENABLE:
|
||||
# LDAP_HOST_URL = CONF.get('ldap', 'host_url')
|
||||
# LDAP_BASE_DN = CONF.get('ldap', 'base_dn')
|
||||
# LDAP_ROOT_DN = CONF.get('ldap', 'root_dn')
|
||||
# LDAP_ROOT_PW = CONF.get('ldap', 'root_pw')
|
||||
# ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW)
|
||||
# else:
|
||||
# ldap_conn = None
|
||||
|
||||
log_level = CONF.get('base', 'log')
|
||||
logger = set_log(log_level)
|
|
@ -4,8 +4,9 @@ from jumpserver.api import *
|
|||
|
||||
|
||||
def name_proc(request):
|
||||
user_id = request.session.get('user_id')
|
||||
role_id = request.session.get('role_id')
|
||||
user_id = request.user.id
|
||||
# role_id = request.session.get('role_id')
|
||||
role_id = {'SU':2,'GA':1,'CU':0}.get(request.user.role,0)
|
||||
# if role_id == 2:
|
||||
user_total_num = User.objects.all().count()
|
||||
user_active_num = User.objects.filter().count()
|
||||
|
|
|
@ -6,7 +6,8 @@ from django.db import models
|
|||
class Setting(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
default_user = models.CharField(max_length=100, null=True, blank=True)
|
||||
default_port = models.IntegerField(null=True, blank=True)
|
||||
default_port = models.IntegerField(max_length=10, null=True, blank=True)
|
||||
default_password = models.CharField(max_length=100, null=True, blank=True)
|
||||
default_pri_key_path = models.CharField(max_length=100, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -11,12 +11,11 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
|
|||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import ConfigParser
|
||||
import djcelery
|
||||
import getpass
|
||||
|
||||
djcelery.setup_loader()
|
||||
config = ConfigParser.ConfigParser()
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
config.read(os.path.join(BASE_DIR, 'jumpserver.conf'))
|
||||
|
||||
|
||||
|
@ -25,13 +24,29 @@ DB_PORT = config.getint('db', 'port')
|
|||
DB_USER = config.get('db', 'user')
|
||||
DB_PASSWORD = config.get('db', 'password')
|
||||
DB_DATABASE = config.get('db', 'database')
|
||||
|
||||
AUTH_USER_MODEL = 'juser.User'
|
||||
# mail config
|
||||
MAIL_ENABLE = config.get('mail', 'mail_enable')
|
||||
EMAIL_HOST = config.get('mail', 'email_host')
|
||||
EMAIL_PORT = config.get('mail', 'email_port')
|
||||
EMAIL_HOST_USER = config.get('mail', 'email_host_user')
|
||||
EMAIL_HOST_PASSWORD = config.get('mail', 'email_host_password')
|
||||
EMAIL_USE_TLS = config.getboolean('mail', 'email_use_tls')
|
||||
EMAIL_TIMEOUT = 5
|
||||
|
||||
# ======== Log ==========
|
||||
LOG = False
|
||||
LOG_DIR = os.path.join(BASE_DIR, 'logs')
|
||||
JLOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log')
|
||||
SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys')
|
||||
# SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server')
|
||||
KEY = config.get('base', 'key')
|
||||
LOGIN_NAME = getpass.getuser()
|
||||
# LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable')
|
||||
URL = config.get('base', 'url')
|
||||
log_dir = os.path.join(BASE_DIR, 'logs')
|
||||
log_level = config.get('base', 'log')
|
||||
web_socket_host = config.get('websocket', 'web_socket_host')
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
|
@ -46,7 +61,6 @@ TEMPLATE_DEBUG = True
|
|||
|
||||
ALLOWED_HOSTS = ['0.0.0.0/8']
|
||||
|
||||
BROKER_URL = 'django://'
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
|
@ -57,21 +71,20 @@ INSTALLED_APPS = (
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'djcelery',
|
||||
'kombu.transport.django',
|
||||
'jumpserver',
|
||||
'juser',
|
||||
'jasset',
|
||||
'jperm',
|
||||
'jlog',
|
||||
'jlog',
|
||||
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
#'django.middleware.csrf.CsrfViewMiddleware',
|
||||
# 'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
#'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
# 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
@ -95,6 +108,12 @@ DATABASES = {
|
|||
}
|
||||
}
|
||||
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.sqlite3',
|
||||
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
# }
|
||||
# }
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
|
@ -103,14 +122,14 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
|||
'django.core.context_processors.static',
|
||||
'django.core.context_processors.tz',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'jumpserver.context_processors.name_proc'
|
||||
'jumpserver.context_processors.name_proc',
|
||||
)
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
os.path.join(BASE_DIR, 'templates'),
|
||||
)
|
||||
|
||||
#STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, "static"),
|
||||
|
@ -133,5 +152,3 @@ USE_TZ = False
|
|||
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
|
||||
|
|
|
@ -8,16 +8,17 @@ urlpatterns = patterns('',
|
|||
(r'^skin_config/$', 'jumpserver.views.skin_config'),
|
||||
(r'^install/$', 'jumpserver.views.install'),
|
||||
(r'^base/$', 'jumpserver.views.base'),
|
||||
(r'^login/$', 'jumpserver.views.login'),
|
||||
(r'^logout/$', 'jumpserver.views.logout'),
|
||||
(r'^login/$', 'jumpserver.views.Login'),
|
||||
(r'^logout/$', 'jumpserver.views.Logout'),
|
||||
(r'^file/upload/$', 'jumpserver.views.upload'),
|
||||
(r'^file/download/$', 'jumpserver.views.download'),
|
||||
(r'^setting', 'jumpserver.views.setting'),
|
||||
(r'^error/$', 'jumpserver.views.httperror'),
|
||||
(r'^juser/', include('juser.urls')),
|
||||
(r'^jasset/', include('jasset.urls')),
|
||||
# (r'^jlog/', include('jlog.urls')),
|
||||
(r'^jlog/', include('jlog.urls')),
|
||||
(r'^jperm/', include('jperm.urls')),
|
||||
(r'^node_auth/', 'jumpserver.views.node_auth'),
|
||||
(r'test2', 'jumpserver.views.test2'),
|
||||
|
||||
)
|
||||
|
|
|
@ -13,43 +13,70 @@ from django.http import HttpResponse
|
|||
import paramiko
|
||||
from jumpserver.api import *
|
||||
from jumpserver.models import Setting
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from settings import BASE_DIR
|
||||
from jlog.models import Log
|
||||
|
||||
|
||||
def getDaysByNum(num):
|
||||
"""
|
||||
输出格式:([datetime.date(2015, 11, 6), datetime.date(2015, 11, 8)], ['11-06', '11-08'])
|
||||
"""
|
||||
|
||||
today = datetime.date.today()
|
||||
oneday = datetime.timedelta(days=1)
|
||||
li_date, li_str = [], []
|
||||
date_li, date_str = [], []
|
||||
for i in range(0, num):
|
||||
today = today-oneday
|
||||
li_date.append(today)
|
||||
li_str.append(str(today)[5:10])
|
||||
li_date.reverse()
|
||||
li_str.reverse()
|
||||
t = (li_date, li_str)
|
||||
return t
|
||||
date_li.append(today)
|
||||
date_str.append(str(today)[5:10])
|
||||
date_li.reverse()
|
||||
date_str.reverse()
|
||||
return date_li, date_str
|
||||
|
||||
|
||||
def get_data(data, items, option):
|
||||
dic = {}
|
||||
li_date, li_str = getDaysByNum(7)
|
||||
for item in items:
|
||||
li = []
|
||||
name = item[option]
|
||||
if option == 'user':
|
||||
option_data = data.filter(user=name)
|
||||
elif option == 'host':
|
||||
option_data = data.filter(host=name)
|
||||
for t in li_date:
|
||||
year, month, day = t.year, t.month, t.day
|
||||
times = option_data.filter(start_time__year=year, start_time__month=month, start_time__day=day).count()
|
||||
li.append(times)
|
||||
dic[name] = li
|
||||
return dic
|
||||
def get_data(x, y, z):
|
||||
pass
|
||||
|
||||
|
||||
def get_data_by_day(date_li, item):
|
||||
data_li = []
|
||||
for d in date_li:
|
||||
logs = Log.objects.filter(start_time__year=d.year,
|
||||
start_time__month=d.month,
|
||||
start_time__day=d.day)
|
||||
if item == 'user':
|
||||
data_li.append(set([log.user for log in logs]))
|
||||
elif item == 'asset':
|
||||
data_li.append(set([log.host for log in logs]))
|
||||
elif item == 'login':
|
||||
data_li.append(logs)
|
||||
else:
|
||||
pass
|
||||
return data_li
|
||||
|
||||
|
||||
def get_count_by_day(date_li, item):
|
||||
data_li = get_data_by_day(date_li, item)
|
||||
data_count_li = []
|
||||
for data in data_li:
|
||||
data_count_li.append(len(data))
|
||||
return data_count_li
|
||||
|
||||
|
||||
def get_count_by_date(date_li, item):
|
||||
data_li = get_data_by_day(date_li, item)
|
||||
data_count_tmp = []
|
||||
for data in data_li:
|
||||
data_count_tmp.extend(list(data))
|
||||
|
||||
return len(set(data_count_tmp))
|
||||
|
||||
|
||||
@require_role(role='user')
|
||||
def index_cu(request):
|
||||
user_id = request.session.get('user_id')
|
||||
user_id = request.user.id
|
||||
user = get_object(User, id=user_id)
|
||||
login_types = {'L': 'LDAP', 'M': 'MAP'}
|
||||
username = user.username
|
||||
|
@ -64,7 +91,6 @@ def index_cu(request):
|
|||
new_posts.append(post_five)
|
||||
post_five = []
|
||||
new_posts.append(post_five)
|
||||
|
||||
return render_to_response('index_cu.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
|
@ -78,6 +104,7 @@ def index(request):
|
|||
return index_cu(request)
|
||||
|
||||
elif is_role_request(request, 'super'):
|
||||
# dashboard 显示汇总
|
||||
users = User.objects.all()
|
||||
hosts = Asset.objects.all()
|
||||
online = Log.objects.filter(is_finished=0)
|
||||
|
@ -85,68 +112,52 @@ def index(request):
|
|||
online_user = online.values('user').distinct()
|
||||
active_users = User.objects.filter(is_active=1)
|
||||
active_hosts = Asset.objects.filter(is_active=1)
|
||||
|
||||
# 一个月历史汇总
|
||||
date_li, date_str = getDaysByNum(30)
|
||||
date_month = repr(date_str)
|
||||
active_user_per_month = str(get_count_by_day(date_li, 'user'))
|
||||
active_asset_per_month = str(get_count_by_day(date_li, 'asset'))
|
||||
active_login_per_month = str(get_count_by_day(date_li, 'login'))
|
||||
|
||||
# 活跃用户资产图
|
||||
active_user_month = get_count_by_date(date_li, 'user')
|
||||
disabled_user_count = len(users.filter(is_active=False))
|
||||
inactive_user_month = len(users) - active_user_month
|
||||
active_asset_month = get_count_by_date(date_li, 'asset')
|
||||
disabled_asset_count = len(hosts.filter(is_active=False)) if hosts.filter(is_active=False) else 0
|
||||
inactive_asset_month = len(hosts) - active_asset_month if len(hosts) > active_asset_month else 0
|
||||
|
||||
# 一周top10用户和主机
|
||||
week_data = Log.objects.filter(start_time__range=[from_week, datetime.datetime.now()])
|
||||
user_top_ten = week_data.values('user').annotate(times=Count('user')).order_by('-times')[:10]
|
||||
host_top_ten = week_data.values('host').annotate(times=Count('host')).order_by('-times')[:10]
|
||||
|
||||
elif is_role_request(request, 'admin'):
|
||||
return index_cu(request)
|
||||
# user = get_session_user_info(request)[2]
|
||||
# users = User.objects.filter(dept=dept)
|
||||
# hosts = Asset.objects.filter(dept=dept)
|
||||
# online = Log.objects.filter(dept_name=dept_name, is_finished=0)
|
||||
# online_host = online.values('host').distinct()
|
||||
# online_user = online.values('user').distinct()
|
||||
# active_users = users.filter(is_active=1)
|
||||
# active_hosts = hosts.filter(is_active=1)
|
||||
# week_data = Log.objects.filter(dept_name=dept_name, start_time__range=[from_week, datetime.datetime.now()])
|
||||
for user_info in user_top_ten:
|
||||
username = user_info.get('user')
|
||||
last = Log.objects.filter(user=username).latest('start_time')
|
||||
user_info['last'] = last
|
||||
|
||||
# percent of dashboard
|
||||
if users.count() == 0:
|
||||
percent_user, percent_online_user = '0%', '0%'
|
||||
else:
|
||||
percent_user = format(active_users.count() / users.count(), '.0%')
|
||||
percent_online_user = format(online_user.count() / users.count(), '.0%')
|
||||
if hosts.count() == 0:
|
||||
percent_host, percent_online_host = '0%', '0%'
|
||||
else:
|
||||
percent_host = format(active_hosts.count() / hosts.count(), '.0%')
|
||||
percent_online_host = format(online_host.count() / hosts.count(), '.0%')
|
||||
for host_info in host_top_ten:
|
||||
host = host_info.get('host')
|
||||
last = Log.objects.filter(host=host).latest('start_time')
|
||||
host_info['last'] = last
|
||||
|
||||
user_top_ten = week_data.values('user').annotate(times=Count('user')).order_by('-times')[:10]
|
||||
host_top_ten = week_data.values('host').annotate(times=Count('host')).order_by('-times')[:10]
|
||||
user_dic, host_dic = get_data(week_data, user_top_ten, 'user'), get_data(week_data, host_top_ten, 'host')
|
||||
# 一周top5
|
||||
week_users = week_data.values('user').distinct().count()
|
||||
week_hosts = week_data.count()
|
||||
|
||||
# a week data
|
||||
week_users = week_data.values('user').distinct().count()
|
||||
week_hosts = week_data.count()
|
||||
user_top_five = week_data.values('user').annotate(times=Count('user')).order_by('-times')[:5]
|
||||
color = ['label-success', 'label-info', 'label-primary', 'label-default', 'label-warnning']
|
||||
|
||||
user_top_five = week_data.values('user').annotate(times=Count('user')).order_by('-times')[:5]
|
||||
color = ['label-success', 'label-info', 'label-primary', 'label-default', 'label-warnning']
|
||||
# 最后10次权限申请
|
||||
# perm apply latest 10
|
||||
# perm_apply_10 = Apply.objects.order_by('-date_add')[:10]
|
||||
|
||||
# perm apply latest 10
|
||||
# perm_apply_10 = Apply.objects.order_by('-date_add')[:10]
|
||||
# 最后10次登陆
|
||||
login_10 = Log.objects.order_by('-start_time')[:10]
|
||||
login_more_10 = Log.objects.order_by('-start_time')[10:21]
|
||||
|
||||
# latest 10 login
|
||||
login_10 = Log.objects.order_by('-start_time')[:10]
|
||||
login_more_10 = Log.objects.order_by('-start_time')[10:21]
|
||||
|
||||
# a week top 10
|
||||
for user_info in user_top_ten:
|
||||
username = user_info.get('user')
|
||||
last = Log.objects.filter(user=username).latest('start_time')
|
||||
user_info['last'] = last
|
||||
|
||||
top = {'user': '活跃用户数', 'host': '活跃主机数', 'times': '登录次数'}
|
||||
top_dic = {}
|
||||
for key, value in top.items():
|
||||
li = []
|
||||
for t in li_date:
|
||||
year, month, day = t.year, t.month, t.day
|
||||
if key != 'times':
|
||||
times = week_data.filter(start_time__year=year, start_time__month=month, start_time__day=day).values(key).distinct().count()
|
||||
else:
|
||||
times = week_data.filter(start_time__year=year, start_time__month=month, start_time__day=day).count()
|
||||
li.append(times)
|
||||
top_dic[value] = li
|
||||
return render_to_response('index.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
|
@ -193,69 +204,95 @@ def is_latest():
|
|||
pass
|
||||
|
||||
|
||||
def login(request):
|
||||
def Login(request):
|
||||
"""登录界面"""
|
||||
if request.session.get('username'):
|
||||
error = ''
|
||||
if request.user.is_authenticated():
|
||||
return HttpResponseRedirect('/')
|
||||
if request.method == 'GET':
|
||||
return render_to_response('login.html')
|
||||
else:
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('password')
|
||||
user_filter = User.objects.filter(username=username)
|
||||
if user_filter:
|
||||
user = user_filter[0]
|
||||
if PyCrypt.md5_crypt(password) == user.password:
|
||||
request.session['user_id'] = user.id
|
||||
user_filter.update(last_login=datetime.datetime.now())
|
||||
if user.role == 'SU':
|
||||
request.session['role_id'] = 2
|
||||
elif user.role == 'GA':
|
||||
request.session['role_id'] = 1
|
||||
else:
|
||||
request.session['role_id'] = 0
|
||||
response = HttpResponseRedirect('/', )
|
||||
response.set_cookie('username', username, expires=604800)
|
||||
response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800)
|
||||
return response
|
||||
if username and password:
|
||||
user = authenticate(username=username, password=password)
|
||||
if user is not None:
|
||||
if user.is_active:
|
||||
login(request, user)
|
||||
# c = {}
|
||||
# c.update(csrf(request))
|
||||
# request.session['csrf_token'] = str(c.get('csrf_token'))
|
||||
# user_filter = User.objects.filter(username=username)
|
||||
# if user_filter:
|
||||
# user = user_filter[0]
|
||||
# if PyCrypt.md5_crypt(password) == user.password:
|
||||
# request.session['user_id'] = user.id
|
||||
# user_filter.update(last_login=datetime.datetime.now())
|
||||
if user.role == 'SU':
|
||||
request.session['role_id'] = 2
|
||||
elif user.role == 'GA':
|
||||
request.session['role_id'] = 1
|
||||
else:
|
||||
request.session['role_id'] = 0
|
||||
return HttpResponseRedirect('/', )
|
||||
# response.set_cookie('username', username, expires=604800)
|
||||
# response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800)
|
||||
# return response
|
||||
else:
|
||||
error = '密码错误,请重新输入。'
|
||||
else:
|
||||
error = '用户不存在。'
|
||||
error = '用户名或密码错误'
|
||||
return render_to_response('login.html', {'error': error})
|
||||
|
||||
|
||||
def logout(request):
|
||||
def Logout(request):
|
||||
request.session.delete()
|
||||
logout(request)
|
||||
return HttpResponseRedirect('/login/')
|
||||
|
||||
|
||||
def setting(request):
|
||||
header_title, path1 = '项目设置', '设置'
|
||||
setting_r = get_object(Setting, name='default')
|
||||
setting_default = get_object(Setting, name='default')
|
||||
|
||||
if request.method == "POST":
|
||||
username = request.POST.get('username', '')
|
||||
port = request.POST.get('port', '')
|
||||
private_key = request.POST.get('key', '')
|
||||
setting_raw = request.POST.get('setting', '')
|
||||
if setting_raw == 'default':
|
||||
username = request.POST.get('username', '')
|
||||
port = request.POST.get('port', '')
|
||||
password = request.POST.get('password', '')
|
||||
private_key = request.POST.get('key', '')
|
||||
|
||||
if '' in [username, port, private_key]:
|
||||
return HttpResponse('所填内容不能为空')
|
||||
else:
|
||||
settings = get_object(Setting, id=1)
|
||||
private_key_path = os.path.join(BASE_DIR, 'keys', 'default', 'default_private_key.pem')
|
||||
with open(private_key_path, 'w') as f:
|
||||
f.write(private_key)
|
||||
os.chmod(private_key_path, 0600)
|
||||
if settings:
|
||||
Setting.objects.filter(name='default').update(default_user=username, default_port=port,
|
||||
default_pri_key_path=private_key_path)
|
||||
if '' in [username, port] and ('' in password or '' in private_key):
|
||||
return HttpResponse('所填内容不能为空, 且密码和私钥填一个')
|
||||
else:
|
||||
setting_r = Setting(name='default', default_user=username, default_port=port,
|
||||
default_pri_key_path=private_key_path).save()
|
||||
private_key_path = os.path.join(BASE_DIR, 'keys', 'default', 'default_private_key.pem')
|
||||
if private_key:
|
||||
with open(private_key_path, 'w') as f:
|
||||
f.write(private_key)
|
||||
os.chmod(private_key_path, 0600)
|
||||
|
||||
if setting_default:
|
||||
if password != setting_default.default_password:
|
||||
password_encode = CRYPTOR.encrypt(password)
|
||||
else:
|
||||
password_encode = password
|
||||
Setting.objects.filter(name='default').update(default_user=username, default_port=port,
|
||||
default_password=password_encode,
|
||||
default_pri_key_path=private_key_path)
|
||||
|
||||
else:
|
||||
password_encode = CRYPTOR.encrypt(password)
|
||||
setting_r = Setting(name='default', default_user=username, default_port=port,
|
||||
default_password=password_encode,
|
||||
default_pri_key_path=private_key_path).save()
|
||||
|
||||
msg = "设置成功"
|
||||
return my_render('setting.html', locals(), request)
|
||||
|
||||
|
||||
def test2(request):
|
||||
return my_render('test2.html', locals(), request)
|
||||
#
|
||||
# def filter_ajax_api(request):
|
||||
# attr = request.GET.get('attr', 'user')
|
||||
|
|
106
juser/models.py
106
juser/models.py
|
@ -1,39 +1,27 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
||||
|
||||
|
||||
class UserGroup(models.Model):
|
||||
name = models.CharField(max_length=80, unique=True)
|
||||
# assets = models.TextField(max_length=1000, verbose_name="Assets", default='')
|
||||
# asset_groups = models.CharField(max_length=1000, verbose_name="Asset Groups", default='')
|
||||
comment = models.CharField(max_length=160, blank=True, null=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
# def get_user(self):
|
||||
# return self.user_set.all()
|
||||
#
|
||||
# def update(self, **kwargs):
|
||||
# for key, value in kwargs.items():
|
||||
# self.__setattr__(key, value)
|
||||
# self.save()
|
||||
|
||||
|
||||
class User(models.Model):
|
||||
class User(AbstractUser):
|
||||
USER_ROLE_CHOICES = (
|
||||
('SU', 'SuperUser'),
|
||||
('GA', 'GroupAdmin'),
|
||||
('CU', 'CommonUser'),
|
||||
)
|
||||
username = models.CharField(max_length=80, unique=True)
|
||||
password = models.CharField(max_length=100)
|
||||
name = models.CharField(max_length=80)
|
||||
email = models.EmailField(max_length=75)
|
||||
role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU')
|
||||
uuid = models.CharField(max_length=100)
|
||||
role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU')
|
||||
group = models.ManyToManyField(UserGroup)
|
||||
ssh_key_pwd = models.CharField(max_length=200)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
@ -41,94 +29,10 @@ class User(models.Model):
|
|||
date_joined = models.DateTimeField(null=True)
|
||||
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.username
|
||||
|
||||
# def get_asset_group(self):
|
||||
# """
|
||||
# Get user host_groups.
|
||||
# 获取用户有权限的主机组
|
||||
# """
|
||||
# host_group_list = []
|
||||
# perm_list = []
|
||||
# user_group_all = self.group.all()
|
||||
# for user_group in user_group_all:
|
||||
# perm_list.extend(user_group.perm_set.all())
|
||||
#
|
||||
# for perm in perm_list:
|
||||
# host_group_list.append(perm.asset_group)
|
||||
#
|
||||
# return host_group_list
|
||||
#
|
||||
# def get_asset_group_info(self, printable=False):
|
||||
# """
|
||||
# Get or print asset group info
|
||||
# 获取或打印用户授权资产组
|
||||
# """
|
||||
# asset_groups_info = {}
|
||||
# asset_groups = self.get_asset_group()
|
||||
#
|
||||
# for asset_group in asset_groups:
|
||||
# asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment]
|
||||
#
|
||||
# if printable:
|
||||
# for group_id in asset_groups_info:
|
||||
# if asset_groups_info[group_id][1]:
|
||||
# print "[%3s] %s -- %s" % (group_id,
|
||||
# asset_groups_info[group_id][0],
|
||||
# asset_groups_info[group_id][1])
|
||||
# else:
|
||||
# print "[%3s] %s" % (group_id, asset_groups_info[group_id][0])
|
||||
# print ''
|
||||
# else:
|
||||
# return asset_groups_info
|
||||
#
|
||||
# def get_asset(self):
|
||||
# """
|
||||
# Get the assets of under the user control.
|
||||
# 获取主机列表
|
||||
# """
|
||||
# assets = []
|
||||
# asset_groups = self.get_asset_group()
|
||||
#
|
||||
# for asset_group in asset_groups:
|
||||
# assets.extend(asset_group.asset_set.all())
|
||||
#
|
||||
# return assets
|
||||
#
|
||||
# def get_asset_info(self, printable=False):
|
||||
# """
|
||||
# Get or print the user asset info
|
||||
# 获取或打印用户资产信息
|
||||
# """
|
||||
# from jasset.models import AssetAlias
|
||||
# assets_info = {}
|
||||
# assets = self.get_asset()
|
||||
#
|
||||
# for asset in assets:
|
||||
# asset_alias = AssetAlias.objects.filter(user=self, asset=asset)
|
||||
# if asset_alias and asset_alias[0].alias != '':
|
||||
# assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)]
|
||||
# else:
|
||||
# assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)]
|
||||
#
|
||||
# if printable:
|
||||
# ips = assets_info.keys()
|
||||
# ips.sort()
|
||||
# for ip in ips:
|
||||
# if assets_info[ip][2]:
|
||||
# print '%-15s -- %s' % (ip, assets_info[ip][2])
|
||||
# else:
|
||||
# print '%-15s' % ip
|
||||
# print ''
|
||||
# else:
|
||||
# return assets_info
|
||||
#
|
||||
# def update(self, **kwargs):
|
||||
# for key, value in kwargs.items():
|
||||
# self.__setattr__(key, value)
|
||||
# self.save()
|
||||
|
||||
|
||||
class AdminGroup(models.Model):
|
||||
"""
|
||||
|
@ -141,5 +45,3 @@ class AdminGroup(models.Model):
|
|||
|
||||
def __unicode__(self):
|
||||
return '%s: %s' % (self.user.username, self.group.name)
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,6 @@ urlpatterns = patterns('juser.views',
|
|||
(r'^profile/$', 'profile'),
|
||||
(r'^change_info/$', 'change_info'),
|
||||
(r'^regen_ssh_key/$', 'regen_ssh_key'),
|
||||
(r'^chg_role/$', 'chg_role'),
|
||||
(r'^change_role/$', 'chg_role'),
|
||||
(r'^down_key/$', 'down_key'),
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ 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):
|
||||
|
@ -60,6 +61,7 @@ def db_add_user(**kwargs):
|
|||
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 = []
|
||||
|
@ -84,10 +86,10 @@ def db_update_user(**kwargs):
|
|||
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 = User.objects.get(id=user_id)
|
||||
if user:
|
||||
user.update(**kwargs)
|
||||
user = user[0]
|
||||
user.set_password(kwargs.pop('password'))
|
||||
user.save()
|
||||
else:
|
||||
return None
|
||||
|
|
|
@ -2,25 +2,25 @@
|
|||
# Author: Guanghongwei
|
||||
# Email: ibuler@qq.com
|
||||
|
||||
import random
|
||||
from Crypto.PublicKey import RSA
|
||||
# import random
|
||||
# from Crypto.PublicKey import RSA
|
||||
import uuid as uuid_r
|
||||
|
||||
from django.db.models import Q
|
||||
from django.template import RequestContext
|
||||
from django.db.models import ObjectDoesNotExist
|
||||
|
||||
from jumpserver.settings import EMAIL_HOST_USER
|
||||
from juser.user_api import *
|
||||
from jperm.perm_api import _public_perm_api, perm_user_api, user_permed
|
||||
|
||||
MAIL_FROM = EMAIL_HOST_USER
|
||||
|
||||
def chg_role(request):
|
||||
role = {'SU': 2, 'DA': 1, 'CU': 0}
|
||||
user, dept = get_session_user_dept(request)
|
||||
role = {'SU': 2, 'GA': 1, 'CU': 0}
|
||||
if request.session['role_id'] > 0:
|
||||
request.session['role_id'] = 0
|
||||
elif request.session['role_id'] == 0:
|
||||
request.session['role_id'] = role.get(user.role, 0)
|
||||
request.session['role_id'] = role.get(request.user.role, 0)
|
||||
return HttpResponseRedirect('/')
|
||||
|
||||
|
||||
|
@ -168,8 +168,8 @@ def user_add(request):
|
|||
if '' in [username, password, ssh_key_pwd, name, role]:
|
||||
error = u'带*内容不能为空'
|
||||
raise ServerError
|
||||
user_test = get_object(User, username=username)
|
||||
if user_test:
|
||||
check_user_is_exist = User.objects.filter(username=username)
|
||||
if check_user_is_exist:
|
||||
error = u'用户 %s 已存在' % username
|
||||
raise ServerError
|
||||
|
||||
|
@ -178,10 +178,10 @@ def user_add(request):
|
|||
else:
|
||||
try:
|
||||
user = db_add_user(username=username, name=name,
|
||||
password=CRYPTOR.md5_crypt(password),
|
||||
password=password,
|
||||
email=email, role=role, uuid=uuid,
|
||||
groups=groups, admin_groups=admin_groups,
|
||||
ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd),
|
||||
ssh_key_pwd=ssh_key_pwd,
|
||||
is_active=is_active,
|
||||
date_joined=datetime.datetime.now())
|
||||
server_add_user(username, password, ssh_key_pwd, ssh_key_login_need)
|
||||
|
@ -233,10 +233,10 @@ def user_list(request):
|
|||
@require_role(role='user')
|
||||
def user_detail(request):
|
||||
header_title, path1, path2 = '用户详情', '用户管理', '用户详情'
|
||||
if request.session.get('role_id') == 0:
|
||||
user_id = request.session.get('user_id')
|
||||
else:
|
||||
user_id = request.GET.get('id', '')
|
||||
# if request.session.get('role_id') == 0:
|
||||
# user_id = request.user.id
|
||||
# else:
|
||||
# user_id = request.GET.get('id', '')
|
||||
# if request.session.get('role_id') == 1:
|
||||
# user, dept = get_session_user_dept(request)
|
||||
# if not validate(request, user=[user_id]):
|
||||
|
@ -244,9 +244,9 @@ def user_detail(request):
|
|||
# if not user_id:
|
||||
# return HttpResponseRedirect('/juser/user_list/')
|
||||
|
||||
user = get_object(User, id=user_id)
|
||||
if user:
|
||||
pass
|
||||
# user = get_object(User, id=user_id)
|
||||
# if user:
|
||||
# pass
|
||||
# asset_group_permed = user.get_asset_group()
|
||||
# logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10]
|
||||
# logs_all = Log.objects.filter(user=user.name).order_by('-start_time')
|
||||
|
@ -257,8 +257,14 @@ def user_detail(request):
|
|||
|
||||
@require_role(role='admin')
|
||||
def user_del(request):
|
||||
user_ids = request.GET.get('id', '')
|
||||
user_id_list = user_ids.split(',')
|
||||
if request.method == "GET":
|
||||
user_ids = request.GET.get('id', '')
|
||||
user_id_list = user_ids.split(',')
|
||||
elif request.method == "POST":
|
||||
user_ids = request.POST.get('id', '')
|
||||
user_id_list = user_ids.split(',')
|
||||
else:
|
||||
return HttpResponse('错误请求')
|
||||
for user_id in user_id_list:
|
||||
user = get_object(User, id=user_id)
|
||||
if user:
|
||||
|
@ -370,11 +376,11 @@ def user_edit(request):
|
|||
else:
|
||||
return HttpResponseRedirect('/juser/user_list/')
|
||||
|
||||
if password != user.password:
|
||||
password_decode = password
|
||||
password = CRYPTOR.md5_crypt(password)
|
||||
else:
|
||||
password_decode = None
|
||||
# if password != user.password:
|
||||
# password_decode = password
|
||||
# password = CRYPTOR.md5_crypt(password)
|
||||
# else:
|
||||
# password_decode = None
|
||||
|
||||
db_update_user(user_id=user_id,
|
||||
password=password,
|
||||
|
@ -409,7 +415,10 @@ def user_edit_adm(request):
|
|||
|
||||
|
||||
def profile(request):
|
||||
user_id = request.session.get('user_id')
|
||||
a = request.user.id
|
||||
a = request.user.groups
|
||||
|
||||
user_id = request.user.id
|
||||
if not user_id:
|
||||
return HttpResponseRedirect('/')
|
||||
user = User.objects.get(id=user_id)
|
||||
|
@ -418,7 +427,7 @@ def profile(request):
|
|||
|
||||
def change_info(request):
|
||||
header_title, path1, path2 = '修改信息', '用户管理', '修改个人信息'
|
||||
user_id = request.session.get('user_id')
|
||||
user_id = request.user.id
|
||||
user = get_object(User, id=user_id)
|
||||
error = ''
|
||||
if not user:
|
||||
|
@ -436,10 +445,11 @@ def change_info(request):
|
|||
error = '密码须大于6位'
|
||||
|
||||
if not error:
|
||||
if password != user.password:
|
||||
password = CRYPTOR.md5_crypt(password)
|
||||
# if password != user.password:
|
||||
# password = CRYPTOR.md5_crypt(password)
|
||||
|
||||
user.update(name=name, password=password, email=email)
|
||||
user.update(name=name, email=email)
|
||||
user.set_password(password)
|
||||
msg = '修改成功'
|
||||
|
||||
return render_to_response('juser/change_info.html', locals(), context_instance=RequestContext(request))
|
||||
|
@ -465,7 +475,7 @@ def down_key(request):
|
|||
user_id = request.GET.get('id')
|
||||
|
||||
if is_role_request(request, 'user'):
|
||||
user_id = request.session.get('user_id')
|
||||
user_id = request.user.id
|
||||
|
||||
if user_id:
|
||||
user = get_object(User, id=user_id)
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
# coding: utf-8
|
||||
|
||||
import time
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import os.path
|
||||
import threading
|
||||
import urllib
|
||||
|
||||
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
|
||||
|
||||
# from gevent import monkey
|
||||
# monkey.patch_all()
|
||||
# import gevent
|
||||
# from gevent.socket import wait_read, wait_write
|
||||
import struct, fcntl, signal, socket, select, fnmatch
|
||||
|
||||
import paramiko
|
||||
from connect import Tty
|
||||
from connect import TtyLog, Log
|
||||
|
||||
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(func):
|
||||
def _deco(request, *args, **kwargs):
|
||||
username = request.get_argument('username', '')
|
||||
asset_name = request.get_argument('asset_name', '')
|
||||
token = request.get_argument('token', '')
|
||||
print username, asset_name, token
|
||||
client = tornado.httpclient.HTTPClient()
|
||||
# response = client.fetch('http://some/url') + urllib.urlencode({'username': username,
|
||||
# 'asset_name': asset_name, 'token': token})
|
||||
# return request.close()
|
||||
return func(request, *args, **kwargs)
|
||||
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_CREATE(self, event):
|
||||
print "Create file:%s." % os.path.join(event.path, event.name)
|
||||
|
||||
def process_IN_DELETE(self, event):
|
||||
print "Delete file:%s." % os.path.join(event.path, event.name)
|
||||
|
||||
def process_IN_MODIFY(self, event):
|
||||
print "Modify file:%s." % os.path.join(event.path, event.name)
|
||||
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):
|
||||
print "You should monitor a file"
|
||||
sys.exit(3)
|
||||
else:
|
||||
print "now starting monitor %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 Application(tornado.web.Application):
|
||||
def __init__(self):
|
||||
handlers = [
|
||||
(r'/monitor', MonitorHandler),
|
||||
(r'/terminal', WebTerminalHandler),
|
||||
(r'/kill', WebTerminalKillHandler),
|
||||
]
|
||||
|
||||
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': True,
|
||||
}
|
||||
|
||||
tornado.web.Application.__init__(self, handlers, **setting)
|
||||
|
||||
|
||||
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
|
||||
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])
|
||||
|
||||
print len(MonitorHandler.threads), len(MonitorHandler.clients)
|
||||
|
||||
def on_message(self, message):
|
||||
# 监控日志,发生变动发向客户端
|
||||
pass
|
||||
|
||||
def on_close(self):
|
||||
# 客户端主动关闭
|
||||
# self.close()
|
||||
|
||||
print "Close websocket."
|
||||
client_index = MonitorHandler.clients.index(self)
|
||||
MonitorHandler.clients.remove(self)
|
||||
MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
|
||||
|
||||
|
||||
class WebTty(Tty):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(WebTty, self).__init__(*args, **kwargs)
|
||||
self.login_type = 'web'
|
||||
self.ws = None
|
||||
self.input_r = ''
|
||||
self.input_mode = False
|
||||
|
||||
|
||||
class WebTerminalKillHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
ws_id = self.get_argument('id')
|
||||
Log.objects.filter(id=ws_id).update(is_finished=True)
|
||||
for ws in WebTerminalHandler.clients:
|
||||
print ws.id
|
||||
if ws.id == int(ws_id):
|
||||
print "killed"
|
||||
ws.log.save()
|
||||
ws.close()
|
||||
print len(WebTerminalHandler.clients)
|
||||
|
||||
|
||||
class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||
tasks = []
|
||||
clients = []
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.term = None
|
||||
self.channel = None
|
||||
self.log_file_f = None
|
||||
self.log_time_f = None
|
||||
self.log = None
|
||||
self.id = 0
|
||||
super(WebTerminalHandler, self).__init__(*args, **kwargs)
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
@require_auth
|
||||
def open(self):
|
||||
asset_name = self.get_argument('asset_name', '')
|
||||
username = self.get_argument('username', '')
|
||||
token = self.get_argument('token', '')
|
||||
print asset_name, username, token
|
||||
self.term = WebTty('a', 'b')
|
||||
self.term.get_connection()
|
||||
self.channel = self.term.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
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
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']:
|
||||
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=self.term.remove_control_char(self.term.input_r)).save()
|
||||
self.term.input_r = ''
|
||||
self.term.input_mode = False
|
||||
self.channel.send(data['data'])
|
||||
|
||||
def on_close(self):
|
||||
print 'On_close'
|
||||
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.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def forward_outbound(self):
|
||||
self.log_file_f, self.log_time_f, self.log = self.term.get_log_file()
|
||||
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
|
||||
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.input_r += data
|
||||
data = ''
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
finally:
|
||||
self.close()
|
||||
|
||||
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=1)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
Binary file not shown.
|
@ -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;}
|
Binary file not shown.
After Width: | Height: | Size: 646 B |
Binary file not shown.
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;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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}));
|
||||
};
|
|
@ -24,6 +24,7 @@
|
|||
<!-- highcharts -->
|
||||
<script src="/static/js/highcharts/highcharts.js"></script>
|
||||
|
||||
|
||||
<script src="/static/js/dropzone/dropzone.js"></script>
|
||||
<!-- active menu -->
|
||||
<script>
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
<!-- validator js -->
|
||||
<script src="/static/js/validator/jquery.validator.js"></script>
|
||||
<script src="/static/js/validator/zh_CN.js"></script>
|
||||
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins"><a href="/juser/user_list/">{{ users.count}}</a></h1>
|
||||
<div class="stat-percent font-bold text-success">{{ percent_user }} <i class="fa fa-bolt"></i></div>
|
||||
{# <div class="stat-percent font-bold text-success">{{ percent_user }} <i class="fa fa-bolt"></i></div>#}
|
||||
<small>All user</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins"><a href="/jasset/host_list/">{{ hosts.count }}</a></h1>
|
||||
<div class="stat-percent font-bold text-info">{{ percent_host }} <i class="fa fa-level-up"></i></div>
|
||||
{# <div class="stat-percent font-bold text-info">{{ percent_host }} <i class="fa fa-level-up"></i></div>#}
|
||||
<small>All host</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,8 +40,8 @@
|
|||
<h5>实时在线用户</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins"><a href="/jlog/log_list/online/"> <span id="online_users"></span></a></h1>
|
||||
<div class="stat-percent font-bold text-navy">{{ percent_online_user }} <i class="fa fa-level-up"></i></div>
|
||||
<h1 class="no-margins"><a href="/jlog/log_list/online/"> <span id="online_users">{{ online_user | length }}</span></a></h1>
|
||||
{# <div class="stat-percent font-bold text-navy">{{ percent_online_user }} <i class="fa fa-level-up"></i></div>#}
|
||||
<small>Online user</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,15 +54,15 @@
|
|||
<h5>已连接服务器</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins"><a href="/jlog/log_list/online/"> <span id="online_hosts"></span></a></h1>
|
||||
<div class="stat-percent font-bold text-danger">{{ percent_online_host }} <i class="fa fa-level-down"></i></div>
|
||||
<h1 class="no-margins"><a href="/jlog/log_list/online/"> <span id="online_hosts">{{ online_host | length }}</span></a></h1>
|
||||
{# <div class="stat-percent font-bold text-danger">{{ percent_online_host }} <i class="fa fa-level-down"></i></div>#}
|
||||
<small>Connected host</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-3 border-bottom white-bg dashboard-header" style="margin-left:15px;height: 346px">
|
||||
<div class="col-sm-2 border-bottom white-bg dashboard-header" style="margin-left:15px;height: 346px">
|
||||
<h2>活跃用户TOP5</h2>
|
||||
<small>过去一周共有<span class="text-info">{{ week_users }}</span>位用户登录<span class="text-success">{{ week_hosts }}</span>次服务器.</small>
|
||||
<ul class="list-group clear-list m-t">
|
||||
|
@ -76,7 +76,32 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-9" id="top10" style="margin-left: -15px;height: 345px"></div>
|
||||
<div class="col-sm-7" id="top10" style="margin-left: -15px;height: 346px;padding: 15px 0 15px 0;"></div>
|
||||
<div class="col-lg-3 white-bg" id="top1" style="margin-left: -15px;height: 346px">
|
||||
<div class="statistic-box">
|
||||
<h4>
|
||||
活跃用户资产占比
|
||||
</h4>
|
||||
<p>
|
||||
以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比
|
||||
</p>
|
||||
<div class="row text-center">
|
||||
<div class="col-lg-6">
|
||||
<div id="activeUser" style="width: 140px; height: 140px;">
|
||||
|
||||
</div>
|
||||
<h5>用户</h5>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div id="activeAsset" style="width: 140px; height: 140px;"></div>
|
||||
<h5>主机</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-t">
|
||||
<small></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
|
@ -126,6 +151,51 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<h5>一周Top10资产</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><i class="fa fa-user"></i> 一周Top10资产 </h3>
|
||||
<small><i class="fa fa-map-marker"></i> 登录次数及最近一次登录记录. </small>
|
||||
</div>
|
||||
<div class="ibox-content inspinia-timeline">
|
||||
{% if host_top_ten %}
|
||||
{% for data in host_top_ten %}
|
||||
<div class="timeline-item">
|
||||
<div class="row">
|
||||
<div class="col-xs-5 date">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<strong>{{ data.host }}</strong>
|
||||
<br/>
|
||||
<small class="text-navy">{{ data.times }}次</small>
|
||||
</div>
|
||||
<div class="col-xs-7 content no-top-border">
|
||||
<p class="m-b-xs">最近一次登录用户</p>
|
||||
<p>{{ data.last.user }}</p>
|
||||
<p>于{{ data.last.start_time |date:"Y-m-d H:i:s" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-center">(暂无)</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="ibox float-e-margins">
|
||||
|
@ -137,7 +207,7 @@
|
|||
</div>
|
||||
<div class="ibox-content ibox-heading">
|
||||
<h3><i class="fa fa-paper-plane-o"></i> 登录记录 </h3>
|
||||
<small<i class="fa fa-map-marker"></i> 最近十次登录记录. </small>
|
||||
<small><i class="fa fa-map-marker"></i> 最近十次登录记录. </small>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<div>
|
||||
|
@ -212,7 +282,7 @@
|
|||
</div>
|
||||
<div class="ibox-content ibox-heading">
|
||||
<h3><i class="fa fa-user"></i> 一周Top10用户 </h3>
|
||||
<small><i class="fa fa-map-marker"></i> 一周Top10用户登录次数及最近一次登录记录. </small>
|
||||
<small><i class="fa fa-map-marker"></i> 用户登录次数及最近一次登录记录. </small>
|
||||
</div>
|
||||
<div class="ibox-content inspinia-timeline">
|
||||
{% if user_top_ten %}
|
||||
|
@ -226,7 +296,7 @@
|
|||
<small class="text-navy">{{ data.times }}次</small>
|
||||
</div>
|
||||
<div class="col-xs-7 content no-top-border">
|
||||
<p class="m-b-xs">最近一次登录</p>
|
||||
<p class="m-b-xs">最近一次登录主机</p>
|
||||
<p>{{ data.last.host }}</p>
|
||||
<p>于{{ data.last.start_time |date:"Y-m-d H:i:s" }}</p>
|
||||
</div>
|
||||
|
@ -247,162 +317,257 @@
|
|||
<!--<div class="col-lg-6" id="hosttop10" style="width:50%;height:400px; margin-top: 20px"></div>-->
|
||||
<!--</div>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block self_footer_js %}
|
||||
<script src="/static/js/echarts/echarts.js"></script>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('#show').click(function(){
|
||||
$('#show').css('display', 'none');
|
||||
$('#more').css('display', 'block');
|
||||
})
|
||||
})
|
||||
var cate = {{ li_str|safe }};
|
||||
$(function () {
|
||||
$('#top10').highcharts({
|
||||
// chart: {
|
||||
// type: 'column'
|
||||
// },
|
||||
title: {
|
||||
text: '一周数据总览',
|
||||
x: -20 //center
|
||||
},
|
||||
subtitle: {
|
||||
text: 'Source: JumpServer',
|
||||
x: -20
|
||||
},
|
||||
rangeSelector: {
|
||||
allButtonsEnabled: true,
|
||||
selected: 2
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
categories: cate
|
||||
},
|
||||
yAxis:{
|
||||
min: 0,
|
||||
title: {
|
||||
text: ''
|
||||
},
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
width: 1,
|
||||
color: '#808080'
|
||||
}]
|
||||
},
|
||||
tooltip: {
|
||||
valueSuffix: '次'
|
||||
},
|
||||
navigation: {
|
||||
buttonOptions: {
|
||||
align: 'right'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{% for k,v in top_dic.items %}
|
||||
{
|
||||
name: '{{ k }}',
|
||||
data: {{ v }}
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
});
|
||||
|
||||
$('#usertop10').highcharts({
|
||||
title: {
|
||||
text: '一周用户登录TOP10',
|
||||
x: -20 //center
|
||||
},
|
||||
subtitle: {
|
||||
text: 'Source: JumpServer',
|
||||
x: -20
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
categories: cate
|
||||
},
|
||||
yAxis:{
|
||||
min: 0,
|
||||
title: {
|
||||
text: '登录次数'
|
||||
},
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
width: 1,
|
||||
color: '#808080'
|
||||
}]
|
||||
},
|
||||
tooltip: {
|
||||
valueSuffix: '次'
|
||||
},
|
||||
series: [
|
||||
{% for k,v in user_dic.items %}
|
||||
{
|
||||
name: '{{ k }}',
|
||||
data: {{ v }}
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
});
|
||||
|
||||
$('#hosttop10').highcharts({
|
||||
title: {
|
||||
text: '一周主机登录TOP10',
|
||||
x: -20 //center
|
||||
},
|
||||
subtitle: {
|
||||
text: 'Source: JumpServer',
|
||||
x: -20
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
categories: cate
|
||||
},
|
||||
yAxis:{
|
||||
min: 0,
|
||||
title: {
|
||||
text: '登录次数'
|
||||
},
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
width: 1,
|
||||
color: '#808080'
|
||||
}]
|
||||
},
|
||||
tooltip: {
|
||||
valueSuffix: '次'
|
||||
},
|
||||
series: [
|
||||
{% for k,v in host_dic.items %}
|
||||
{
|
||||
name: '{{ k }}',
|
||||
data: {{ v }}
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
});
|
||||
|
||||
function magic_number(value, id) {
|
||||
var num = $("#"+id);
|
||||
num.animate({count: value}, {
|
||||
duration: 500,
|
||||
step: function() {
|
||||
num.text(String(parseInt(this.count)));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function update() {
|
||||
$.getJSON('api/user/', function(data) {
|
||||
var users = data.users;
|
||||
var hosts = data.hosts;
|
||||
magic_number(users, 'online_users');
|
||||
magic_number(hosts, 'online_hosts')
|
||||
});
|
||||
};
|
||||
|
||||
setInterval(update, 5000); //5秒钟执行一次
|
||||
update();
|
||||
|
||||
});
|
||||
|
||||
require.config({
|
||||
paths: {
|
||||
'echarts': '/static/js/echarts/chart',
|
||||
'echarts/chart/line': '/static/js/echarts/chart/line',
|
||||
'echarts/chart/pie': '/static/js/echarts/chart/pie'
|
||||
}
|
||||
});
|
||||
require(
|
||||
[
|
||||
'echarts',
|
||||
'echarts/chart/line'
|
||||
],
|
||||
function (ec) {
|
||||
var top10Chart = ec.init(document.getElementById('top10'));
|
||||
var option = {
|
||||
title : {
|
||||
text: '月数据总览',
|
||||
subtext: '一个月内历史汇总',
|
||||
x: 'center'
|
||||
},
|
||||
tooltip : {
|
||||
trigger: 'axis'
|
||||
},
|
||||
backgroundColor: '#fff',
|
||||
legend: {
|
||||
data:['登陆次数', '活跃用户','活跃资产'],
|
||||
y: 'bottom'
|
||||
},
|
||||
toolbox: {
|
||||
show : false,
|
||||
feature : {
|
||||
{# mark : {show: true},#}
|
||||
{# dataView : {show: true, readOnly: false},#}
|
||||
magicType : {show: true, type: ['line', 'bar']}
|
||||
}
|
||||
},
|
||||
calculable : true,
|
||||
xAxis : [
|
||||
{
|
||||
type : 'category',
|
||||
boundaryGap : false,
|
||||
data : {{ date_month | safe}}
|
||||
}
|
||||
],
|
||||
yAxis : [
|
||||
{
|
||||
type : 'value'
|
||||
}
|
||||
],
|
||||
series : [
|
||||
{
|
||||
name:'登陆次数',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: {{ active_login_per_month | safe }}
|
||||
},
|
||||
{
|
||||
name:'活跃用户',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: {{ active_user_per_month | safe }}
|
||||
},
|
||||
{
|
||||
name:'活跃资产',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data: {{ active_asset_per_month | safe }}
|
||||
}
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
top10Chart.setOption(option);
|
||||
}
|
||||
);
|
||||
|
||||
require(
|
||||
[
|
||||
'echarts',
|
||||
'echarts/chart/pie'
|
||||
],
|
||||
function (ec) {
|
||||
var auChart = ec.init(document.getElementById('activeUser'));
|
||||
var option = {
|
||||
tooltip : {
|
||||
trigger: 'item',
|
||||
formatter: "{b} <br> {c} ({d}%)"
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
orient : 'vertical',
|
||||
x : 'left',
|
||||
data:['月活跃用户','禁用用户','月未登陆用户']
|
||||
},
|
||||
toolbox: {
|
||||
show : false,
|
||||
feature : {
|
||||
mark : {show: true},
|
||||
dataView : {show: true, readOnly: false},
|
||||
magicType : {
|
||||
show: true,
|
||||
type: ['pie', 'funnel'],
|
||||
option: {
|
||||
funnel: {
|
||||
x: '25%',
|
||||
width: '50%',
|
||||
funnelAlign: 'center',
|
||||
max: 1548
|
||||
}
|
||||
}
|
||||
},
|
||||
restore : {show: true},
|
||||
saveAsImage : {show: true}
|
||||
}
|
||||
},
|
||||
calculable : true,
|
||||
series : [
|
||||
{
|
||||
name:'访问来源',
|
||||
type:'pie',
|
||||
radius : ['50%', '70%'],
|
||||
itemStyle : {
|
||||
normal : {
|
||||
label : {
|
||||
show : false
|
||||
},
|
||||
labelLine : {
|
||||
show : false
|
||||
}
|
||||
},
|
||||
emphasis : {
|
||||
label : {
|
||||
show : true,
|
||||
position : 'center',
|
||||
textStyle : {
|
||||
fontSize : '5',
|
||||
fontWeight : 'bold'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data:[
|
||||
{value:{{ active_user_month }}, name:'月活跃用户'},
|
||||
{value:{{ disabled_user_count }}, name:'禁用用户'},
|
||||
{value:{{ inactive_user_month }}, name:'月未登陆用户'}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
auChart.setOption(option);
|
||||
}
|
||||
);
|
||||
|
||||
require(
|
||||
[
|
||||
'echarts',
|
||||
'echarts/chart/pie'
|
||||
],
|
||||
function (ec) {
|
||||
var aaChart = ec.init(document.getElementById('activeAsset'));
|
||||
var option = {
|
||||
tooltip : {
|
||||
trigger: 'item',
|
||||
formatter: "{b} <br> {c} ({d}%)"
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
orient : 'vertical',
|
||||
x : 'left',
|
||||
data:['月被登陆主机','禁用主机','月未登陆主机']
|
||||
},
|
||||
toolbox: {
|
||||
show : false,
|
||||
feature : {
|
||||
mark : {show: true},
|
||||
dataView : {show: true, readOnly: false},
|
||||
magicType : {
|
||||
show: true,
|
||||
type: ['pie', 'funnel'],
|
||||
option: {
|
||||
funnel: {
|
||||
x: '25%',
|
||||
width: '50%',
|
||||
funnelAlign: 'center',
|
||||
max: 1548
|
||||
}
|
||||
}
|
||||
},
|
||||
restore : {show: true},
|
||||
saveAsImage : {show: true}
|
||||
}
|
||||
},
|
||||
calculable : true,
|
||||
series : [
|
||||
{
|
||||
name:'访问来源',
|
||||
type:'pie',
|
||||
radius : ['50%', '70%'],
|
||||
itemStyle : {
|
||||
normal : {
|
||||
label : {
|
||||
show : false
|
||||
},
|
||||
labelLine : {
|
||||
show : false
|
||||
}
|
||||
},
|
||||
emphasis : {
|
||||
label : {
|
||||
show : true,
|
||||
position : 'center',
|
||||
textStyle : {
|
||||
fontSize : '5',
|
||||
fontWeight : 'bold'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data:[
|
||||
{value:{{ active_asset_month }}, name:'月被登陆主机'},
|
||||
{value:{{ disabled_asset_count }}, name:'禁用主机'},
|
||||
{value:{{ inactive_asset_month }}, name:'月未登陆主机'}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
aaChart.setOption(option);
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -72,7 +72,7 @@
|
|||
<div class="col-lg-4">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label label-primary"><b>{{ user.name }}</b></span>
|
||||
<span class="label label-primary"><b>{{ user.username }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -109,7 +109,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="text-navy">角色</td>
|
||||
<td>{{ user.id | get_role }}</td>
|
||||
<td>{{ user.role }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-navy">Email</td>
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
|
||||
<div class="ibox-content">
|
||||
<div>
|
||||
{% if session_role_id > 0 %}
|
||||
<a target="_blank" href="/jasset/asset_add/" class="btn btn-sm btn-primary "> 添加 </a>
|
||||
{% endif %}
|
||||
<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="keyword" placeholder="Search">
|
||||
|
|
|
@ -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,139 @@
|
|||
{% 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 %}
|
||||
{% block content %}
|
||||
{% include 'nav_cat_bar.html' %}
|
||||
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-lg-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="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
|
||||
<li class="active"><a href="/jlog/search/" class="text-center"><i class="fa fa-bar-chart-o"></i> 详细搜索 </a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="tab-content">
|
||||
<form method="get" action="" role="form" class="form-inline">
|
||||
<p>
|
||||
选择相应条件进行搜索
|
||||
</p>
|
||||
<div class="form-group" id="data_5">
|
||||
<div class="input-daterange input-group" id="datepicker">
|
||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="start" value="{{ date_seven_day }}">
|
||||
<span class="input-group-addon">to</span>
|
||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="end" value="{{ date_now_str }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<select name="single" data-placeholder="用户名" class="chosen-select" multiple style="width:300px;" tabindex="2">
|
||||
<option value="用户">用户名</option>
|
||||
<option value="Bolivia, Plurinational State of">hongweiguang</option>
|
||||
<option value="Bonaire, Sint Eustatius and Saba">wangyong</option>
|
||||
<option value="Bosnia and Herzegovina">hehe</option>
|
||||
<option value="Botswana">wangyong</option>
|
||||
<option value="Bouvet Island">wangyongd</option>
|
||||
<option value="Romania">Romania</option>
|
||||
<option value="Zambia">Zambia</option>
|
||||
<option value="Zimbabwe">Zimbabwe</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<select name="multi" data-placeholder="主机" class="chosen-select" multiple style="width:200px;" tabindex="4">
|
||||
<option value="主机">主机</option>
|
||||
<option value="United States">172.16.1.1</option>
|
||||
<option value="Afghanistan">172.16.1.1</option>
|
||||
<option value="Aland Islands">172.16.1.1</option>
|
||||
<option value="Albania">172.16.1.1</option>
|
||||
<option value="Algeria">172.16.1.1</option>
|
||||
<option value="American Samoa">172.16.1.1</option>
|
||||
<option value="Andorra">172.16.1.1</option>
|
||||
<option value="Angola">172.16.1.1</option>
|
||||
<option value="Anguilla">172.16.1.1</option>
|
||||
<option value="Antarctica">172.16.1.1</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input id="cmd" name="cmd" placeholder="命令" type="text" class="form-control" style="width: 200px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">搜索</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#data_5 .input-daterange').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
autoclose: true
|
||||
});
|
||||
});
|
||||
|
||||
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>
|
||||
{% endblock %}
|
||||
{% block self_footer_js %}
|
||||
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Jumpserver | 开源跳板机系统</title>
|
||||
|
||||
<link rel="shortcut icon" href="/static/img/facio.ico" type="image/x-icon">
|
||||
{% include 'link_css.html' %}
|
||||
{% include 'head_script.html' %}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div class="col-lg-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<h5> 实时监控 </h5>
|
||||
</div>
|
||||
|
||||
<div class="ibox-content blank-panel" id="content" style="background-color: #0b0b0b; color: #006621; height: 500px; padding: 20px;">
|
||||
你好<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
{% block self_footer_js %}
|
||||
<script>
|
||||
function monitor(){
|
||||
var wsUri = 'ws://j:8080/send';
|
||||
var ws = new WebSocket(wsUri);
|
||||
ws.onopen = function(evt){
|
||||
$('#content').append('Connect websocket success' + '<br />');
|
||||
ws.send('Start')
|
||||
};
|
||||
|
||||
ws.onmessage = function(evt){
|
||||
console.log(evt.data);
|
||||
$('#content').append(evt.data.replace(/\n|\r|(\r\n)|(\u0085)|(\u2028)|(\u2029)/g, '<br>'));
|
||||
};
|
||||
|
||||
ws.onclose = function(evt){
|
||||
$('#content').append('Disconnect with websocket')
|
||||
}
|
||||
}
|
||||
|
||||
monitor();
|
||||
</script>
|
||||
{% endblock %}
|
||||
</html>
|
|
@ -1,12 +1,18 @@
|
|||
{% 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 %}
|
||||
{% block content %}
|
||||
{% include 'nav_cat_bar.html' %}
|
||||
|
||||
<style>
|
||||
.bootstrap-dialog-body {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.bootstrap-dialog-message {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
color: #00FF00;
|
||||
}
|
||||
.modal-content {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
|
@ -21,9 +27,7 @@
|
|||
.modal-header {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
.bootstrap-dialog-message {
|
||||
color: #00FF00;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -39,12 +43,6 @@
|
|||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
<li><a href="#">未启用 1</a>
|
||||
</li>
|
||||
<li><a href="#">未启用 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
|
@ -56,48 +54,73 @@
|
|||
<ul class="nav nav-tabs">
|
||||
<li><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||
<li class="active"><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
|
||||
<li 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="search_input" name="keyword" placeholder="Search">
|
||||
<input type="text" style="display: none">
|
||||
<div class="input-group-btn">
|
||||
<button id='search_btn' type="button" class="btn btn-sm btn-primary" onclick="log_search()">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
{# <li><a href="/jlog/search/" class="text-center"><i class="fa fa-bar-chart-o"></i> 详细搜索 </a></li>#}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
<form class="form-inline" action="" method="get">
|
||||
<div class="form-group" id="data_5">
|
||||
<div class="form-group" id="data_5">
|
||||
<div class="input-daterange input-group" id="datepicker">
|
||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="start" value="{{ date_seven_day }}">
|
||||
<span class="input-group-addon">to</span>
|
||||
<input type="text" class="input-sm form-control" style="width: 100px;" name="end" value="{{ date_now_str }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<select name="username" data-placeholder="用户名" class="chosen-select" multiple style="width:200px;" tabindex="2">
|
||||
{% for username in username_all %}
|
||||
<option value="{{ username }}"{% if username in username_list %}selected{% endif %}>{{ username }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<select name="host" data-placeholder="主机" class="chosen-select" multiple style="width:200px;" tabindex="4">
|
||||
{% for ip in ip_all %}
|
||||
<option value="{{ ip }}" {% if ip in host_list %}selected{% endif %}>{{ ip }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input id="cmd" name="cmd" placeholder="命令" type="text" class="form-control" value="{{ cmd }}" style="width: 200px;">
|
||||
</div>
|
||||
</div>
|
||||
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
<div class="tab-content">
|
||||
<table class="table table-striped table-bordered table-hover ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"> 用户名 </th>
|
||||
<th class="text-center"> 所属部门 </th>
|
||||
<th class="text-center"> 登录主机 </th>
|
||||
<th class="text-center"> 来源IP </th>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<th class="text-center"> 命令统计 </th>
|
||||
{% endifnotequal %}
|
||||
<th class="text-center"> 回放录像 </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" id="username"> {{ post.user }} </td>
|
||||
<td class="text-center" id="dept"> {{ post.dept_name }} </td>
|
||||
<td class="text-center" id="ip"> {{ post.host }} </td>
|
||||
<td class="text-center" id="remote_ip"> {{ post.remote_ip }} </td>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </td>
|
||||
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </a></td>
|
||||
{% endifnotequal %}
|
||||
<td class="text-center"><a value="/jlog/record/?id={{ post.id }}" class="log_record"> 回放 </a></td>
|
||||
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s"}} </td>
|
||||
<td class="text-center" id="end_time"> {{ post.end_time|date:"Y-m-d H:i:s" }} </td>
|
||||
</tr>
|
||||
|
@ -116,10 +139,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#<script src="http://{{ web_socket_host }}/socket.io/socket.io.js"></script>#}
|
||||
<script>
|
||||
$('.log_command').on('click',function(){
|
||||
$('.log_record').click(function(){
|
||||
var url = $(this).attr('value');
|
||||
window.open(url, '播放', 'height=500, width=910, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.log_command').on('click',function(){
|
||||
var url = $(this).attr('href');
|
||||
var username = $('#username')[0].innerText;
|
||||
var ip = $('#ip')[0].innerText;
|
||||
|
@ -127,17 +154,15 @@
|
|||
var end_time = $('#end_time')[0].innerText;
|
||||
var div_username = ' 用户名: '+'<span class="text-info">'+username+'' + '</span>';
|
||||
var div_ip = ' 主机: '+'<span class="text-info">' + ip + '</span>';
|
||||
var div_time = ' 开始时间: ' + '<span class="text-info">'+start_time +'</span>' + ' 结束时间: ' +'<span class="text-info">' + end_time + '</span'
|
||||
var div_time = ' 开始时间: ' + '<span class="text-info">'+start_time +'</span>' + ' 结束时间: ' +'<span class="text-info">' + end_time + '</span>';
|
||||
var title = 'JumpServer命令统计 '+ div_username + div_ip + div_time;
|
||||
$.ajax({url:url,success:function(data){
|
||||
var tag = $('<div style="height: 500px;overflow: auto;background-color: rgba(0, 0, 0, 0);"></div>').html(data.replace(/\n/g,"<br />"));
|
||||
BootstrapDialog.show({title: title, message:tag[0]});
|
||||
$.ajax({url:url,
|
||||
success:function(data){
|
||||
var tag = $('<div style="height: 500px;overflow: auto;background-color: rgba(0, 0, 0, 0);"></div>').html(data.replace(/\n/g,"<br />"));
|
||||
BootstrapDialog.show({title: title, message:tag[0]});
|
||||
}});
|
||||
return false;
|
||||
});
|
||||
globalConfig = {
|
||||
SOCKET_HOST: "{{ web_socket_host }}"
|
||||
};
|
||||
|
||||
function log_search(){
|
||||
$.ajax({
|
||||
|
@ -150,11 +175,33 @@
|
|||
});
|
||||
}
|
||||
|
||||
$("#search_input").keydown(function(e){
|
||||
if(e.keyCode==13){
|
||||
log_search()
|
||||
}
|
||||
})
|
||||
{# $("#search_input").keydown(function(e){#}
|
||||
{# if(e.keyCode==13){#}
|
||||
{# log_search()#}
|
||||
{# }#}
|
||||
{# });#}
|
||||
|
||||
$('#data_5 .input-daterange').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
autoclose: true
|
||||
});
|
||||
|
||||
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>
|
||||
{% endblock %}
|
||||
{% block self_footer_js %}
|
||||
<script src="/static/js/cropper/cropper.min.js"></script>
|
||||
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
|
||||
{% endblock %}
|
|
@ -1,18 +1,32 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
{% include 'nav_cat_bar.html' %}
|
||||
<style>
|
||||
{% block self_head_css_js %}
|
||||
<style>
|
||||
.terminal {
|
||||
border: #000 solid 5px;
|
||||
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||
font-size: 11px;
|
||||
color: #f0f0f0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
width: 600px;
|
||||
box-shadow: rgba(0, 0, 0, 0.6) 2px 2px 20px;
|
||||
}
|
||||
|
||||
.reverse-video {
|
||||
color: #000;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.bootstrap-dialog-body {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.bootstrap-dialog-message {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
color: #00FF00;
|
||||
}
|
||||
.pre-class {
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
background-color: #000;
|
||||
}
|
||||
.modal-dialog {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
|
@ -24,14 +38,19 @@
|
|||
.modal-header {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
<script src="/static/js/term.js"></script>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% include 'nav_cat_bar.html' %}
|
||||
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div id="ibox-content" class="ibox-title">
|
||||
<h5> 用户日志详细信息列表 </h5>
|
||||
<h5> 用户日志详细信息列表 <input type="button" id="test_connect" class="btn btn-primary" value="测试连接 web terminal" /> </h5>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -39,12 +58,6 @@
|
|||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
<li><a href="#">未启用 1</a>
|
||||
</li>
|
||||
<li><a href="#">未启用 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
|
@ -56,19 +69,6 @@
|
|||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="/jlog/log_list/online/" class="text-center"><i class="fa fa-laptop"></i> 在线 </a></li>
|
||||
<li><a href="/jlog/log_list/offline/" class="text-center"><i class="fa fa-bar-chart-o"></i> 历史记录 </a></li>
|
||||
<li 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="search_input" name="keyword" placeholder="Search">
|
||||
<input type="text" style="display: none">
|
||||
<div class="input-group-btn">
|
||||
<button id='search_btn' type="button" class="btn btn-sm btn-primary" onclick="log_search()">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
|
@ -77,10 +77,10 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"> 用户名 </th>
|
||||
<th class="text-center"> 所属部门 </th>
|
||||
<th class="text-center"> 登录主机 </th>
|
||||
<th class="text-center"> 来源IP </th>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<th class="text-center"> 统计命令 </th>
|
||||
<th class="text-center"> 实时监控 </th>
|
||||
<th class="text-center"> 阻断 </th>
|
||||
{% endifnotequal %}
|
||||
|
@ -92,14 +92,14 @@
|
|||
{% for post in contacts.object_list %}
|
||||
<tr class="gradeX">
|
||||
<td id="username" class="text-center"> {{ post.user }} </td>
|
||||
<td id="deptname" class="text-center"> {{ post.dept_name }} </td>
|
||||
<td id="ip" class="text-center"> {{ post.host }} </td>
|
||||
<td id="remote_ip" class="text-center"> {{ post.remote_ip }} </td>
|
||||
{% ifnotequal session_role_id 0 %}
|
||||
<td class="text-center"><a class="monitor" filename="{{ post.log_path }}"> 监控 </a></td>
|
||||
<td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}")' /></td>
|
||||
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </a></td>
|
||||
<td class="text-center"><a class="monitor" file_path="{{ post.log_path }}"> 监控 </a></td>
|
||||
<td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}", "{{ post.remote_ip }}")' /></td>
|
||||
{% endifnotequal %}
|
||||
<td class="text-center"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td>
|
||||
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -116,115 +116,108 @@
|
|||
</div>
|
||||
|
||||
|
||||
<script src="http://{{ web_socket_host }}/socket.io/socket.io.js"></script>
|
||||
{#<script src="http://{{ web_socket_host }}/socket.io/socket.io.js"></script>#}
|
||||
<script>
|
||||
$.fn.webSocket = function(opt){
|
||||
var st = {};
|
||||
st = $.extend(st,opt);
|
||||
var message = {};
|
||||
var $this = $(this);
|
||||
var genUid = function(){
|
||||
return new Date().getTime()+""+Math.floor(Math.random()*899+100);
|
||||
};
|
||||
var init = function(e){
|
||||
var socket = io.connect('ws://'+globalConfig.SOCKET_HOST);
|
||||
var node = $(e.target);
|
||||
message.id = genUid();
|
||||
message.filename = node.attr('filename');
|
||||
var username = $('#username')[0].innerText;
|
||||
var ip = $('#ip')[0].innerText;
|
||||
BootstrapDialog.show({message:function(){
|
||||
var option, exsit_message;
|
||||
var escapeString = function (html){
|
||||
var elem = document.createElement('div');
|
||||
var txt = document.createTextNode(html);
|
||||
elem.appendChild(txt);
|
||||
return elem.innerHTML;
|
||||
};
|
||||
var tag = $('<div id="log" style="height: 500px;overflow: auto;background-color: rgba(0, 0, 0, 0);"></div>');
|
||||
{# $(document).ready(function(){#}
|
||||
{# $('.monitor').click(function(){#}
|
||||
{# window.open('/jlog/monitor/', '监控', 'height=500, width=910, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');#}
|
||||
{# })#}
|
||||
{# });#}
|
||||
function init(obj){
|
||||
var file_path = obj.attr('file_path');
|
||||
var wsUri = '{{ web_monitor_uri }}';
|
||||
var socket = new WebSocket(wsUri + '?file_path=' + file_path);
|
||||
|
||||
var username = "";
|
||||
var seed = "";
|
||||
document.cookie.split('; ').forEach(function(obj){
|
||||
var info = obj.split('=');
|
||||
if(info.length == 2 ){
|
||||
if(info[0] == 'username'){
|
||||
username = info[1];
|
||||
}else if(info[0] == 'seed'){
|
||||
seed = info[1];
|
||||
}
|
||||
}
|
||||
})
|
||||
var term = new Terminal({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
screenKeys: false
|
||||
});
|
||||
|
||||
var tag = $('<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>');
|
||||
term.open();
|
||||
term.resize(80, 24);
|
||||
|
||||
socket.onopen = function(evt){
|
||||
socket.send('hello');
|
||||
term.write('~.~ Connect WebSocket Success.~.~ \r\n');
|
||||
};
|
||||
|
||||
window.onbeforeunload = function(){
|
||||
socket.close()
|
||||
};
|
||||
|
||||
var username = obj.closest('tr').find('#username').text();
|
||||
var ip = obj.closest('tr').find('#ip').text();
|
||||
|
||||
BootstrapDialog.show({message: function(){
|
||||
//服务器端认证
|
||||
{# socket.send('login', {userid:message.id, filename:message.filename,username:username,seed:seed});#}
|
||||
window.setTimeout(function(){
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
socket.onmessage = function(evt){
|
||||
term.write(evt.data);
|
||||
}}, 1000);
|
||||
|
||||
//告诉服务器端有用户登录
|
||||
socket.emit('login', {userid:message.id, filename:message.filename,username:username,seed:seed});
|
||||
socket.on('message',function(obj){
|
||||
option = obj.option;
|
||||
console.log(option+'so')
|
||||
exsit_message = obj.content;
|
||||
console.log(obj.content)
|
||||
//去除log中的颜色控制字符
|
||||
var regx = /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/g;
|
||||
// tag.append('<p>'+escapeString(obj.content.replace(regx,''))+'</p>');
|
||||
if (option == 'new') {
|
||||
// tag.append('<p style="margin: 2px">' + escapeString(obj.content) + '</p>');
|
||||
tag.append('<p style="margin: 2px">'+escapeString(obj.content.replace(regx,' '))+'</p>');
|
||||
} else if (option == 'exist') {
|
||||
tag.append('<pre>' + exsit_message + '</pre>');
|
||||
}
|
||||
tag.animate({ scrollTop: tag[0].scrollHeight}, 1);
|
||||
});
|
||||
tag[0].style.color = "#00FF00";
|
||||
return tag[0];
|
||||
|
||||
} ,
|
||||
title:'Jumpserver实时监控 '+' 登录用户名: '+'<span class="text-info">'+username+'</span>'+' 登录主机: '+'<span class="text-info">'+ip,
|
||||
onhide:function(){
|
||||
socket.emit('disconnect');
|
||||
socket.close();
|
||||
}});
|
||||
}
|
||||
$this.on("click",function(e){
|
||||
init(e);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
$('.log_command').on('click',function(){
|
||||
var url = $(this).attr('href');
|
||||
$.ajax({url:url,success:function(data){
|
||||
BootstrapDialog.show({title:'命令统计',message:data});
|
||||
}});
|
||||
return false;
|
||||
})
|
||||
globalConfig = {
|
||||
SOCKET_HOST: "{{ web_socket_host }}"
|
||||
}
|
||||
$(".monitor").webSocket()
|
||||
|
||||
|
||||
function log_search(){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/jlog/search/?env=online",
|
||||
data: $("#search_form").serialize(),
|
||||
success: function (data) {
|
||||
$(".tab-content").html(data);
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$('.monitor').click(function(){
|
||||
init($(this));
|
||||
});
|
||||
}
|
||||
|
||||
$("#search_input").keydown(function(e){
|
||||
if(e.keyCode==13){
|
||||
log_search()
|
||||
}
|
||||
})
|
||||
$('.log_command').on('click',function(){
|
||||
var url = $(this).attr('href');
|
||||
var username = $(this).closest('tr').find('#username').text();
|
||||
var ip = $(this).closest('tr').find('#ip').text();
|
||||
var start_time = $(this).closest('tr').find('#start_time').text();
|
||||
var div_username = ' 用户名: '+'<span class="text-info">'+username+'' + '</span>';
|
||||
var div_ip = ' 主机: '+'<span class="text-info">' + ip + '</span>';
|
||||
var div_time = ' 开始时间: ' + '<span class="text-info">'+start_time +'</span>' + '</span>';
|
||||
var title = 'JumpServer命令统计 '+ div_username + div_ip + div_time;
|
||||
$.ajax({url:url,
|
||||
success:function(data){
|
||||
var tag = $('<div style="height: 500px;overflow: auto;background-color: rgba(0, 0, 0, 0);"></div>').html(data.replace(/\n/g,"<br />"));
|
||||
BootstrapDialog.show({title: title, message:tag[0]});
|
||||
}});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#test_connect').click(function(){
|
||||
window.open('/jlog/web_terminal/?asset_name="hello', '播放', 'height=400, width=600, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
|
||||
});
|
||||
});
|
||||
|
||||
{# function log_search(){#}
|
||||
{# $.ajax({#}
|
||||
{# type: "GET",#}
|
||||
{# url: "/jlog/search/?env=online",#}
|
||||
{# data: $("#search_form").serialize(),#}
|
||||
{# success: function (data) {#}
|
||||
{# $(".tab-content").html(data);#}
|
||||
{# }#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
|
||||
|
||||
function cut(num, host){
|
||||
console.log(host);
|
||||
if (host=='Web'){
|
||||
var g_url = '{{ web_kill_uri }}' + '?id=' + num;
|
||||
} else {
|
||||
g_url = "/jlog/log_kill/?id=" + num;
|
||||
}
|
||||
|
||||
function cut(num){
|
||||
var g_url = "/jlog/log_kill/?id="+num;
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: g_url,
|
||||
success: window.open("/jlog/log_list/online/", "_self")
|
||||
// error: window.open(g_url, "_self")
|
||||
});
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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,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-lg-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>
|
|
@ -1,4 +1,4 @@
|
|||
{% ifequal session_role_id 2 %}
|
||||
{% if request.session.role_id == 2 %}
|
||||
<nav class="navbar-default navbar-static-side" role="navigation">
|
||||
<div class="sidebar-collapse">
|
||||
<ul class="nav" id="side-menu">
|
||||
|
@ -51,8 +51,8 @@
|
|||
|
||||
</div>
|
||||
</nav>
|
||||
{% endifequal %}
|
||||
{% ifequal session_role_id 1 %}
|
||||
{% endif %}
|
||||
{% if request.session.role_id == 1 %}
|
||||
<nav class="navbar-default navbar-static-side" role="navigation">
|
||||
<div class="sidebar-collapse">
|
||||
<ul class="nav" id="side-menu">
|
||||
|
@ -73,8 +73,8 @@
|
|||
<li id="jasset">
|
||||
<a><i class="fa fa-cube"></i> <span class="nav-label">资产管理</span><span class="fa arrow"></span></a>
|
||||
<ul class="nav nav-second-level">
|
||||
<li class="host_add host_add_multi"><a href="/jasset/host_add/">添加资产</a></li>
|
||||
<li class="host_list host_detail host_edit"><a href="/jasset/host_list/">查看资产<span class="label label-info pull-right">{{ host_active_num }}/{{ host_total_num}}</span></a></li>
|
||||
{# <li class="host_add host_add_multi"><a href="/jasset/host_add/">添加资产</a></li>#}
|
||||
<li class="host_list host_detail host_edit"><a href="/jasset/asset_list/">查看资产<span class="label label-info pull-right">{{ host_active_num }}/{{ host_total_num}}</span></a></li>
|
||||
<li class="idc_list idc_detail idc_edit"><a href="/jasset/idc_list/">查看IDC</a></li>
|
||||
<li class="group_add"><a href="/jasset/group_add/">添加主机组</a></li>
|
||||
<li class="group_list group_detail group_edit"><a href="/jasset/group_list/">查看主机组</a></li>
|
||||
|
@ -103,9 +103,9 @@
|
|||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
{% ifequal session_role_id 0 %}
|
||||
{% if request.session.role_id == 0 %}
|
||||
<nav class="navbar-default navbar-static-side" role="navigation">
|
||||
<div class="sidebar-collapse">
|
||||
<ul class="nav" id="side-menu">
|
||||
|
@ -117,7 +117,7 @@
|
|||
<a href="/juser/user_detail/?id={{ session_user_id }}"><i class="fa fa-rebel"></i> <span class="nav-label">个人信息</span><span class="label label-info pull-right"></span></a>
|
||||
</li>
|
||||
<li id="jasset">
|
||||
<a href="/jasset/host_list/"><i class="fa fa-cube"></i> <span class="nav-label">查看主机</span><span class="label label-info pull-right"></span></a>
|
||||
<a href="/jasset/asset_list/"><i class="fa fa-cube"></i> <span class="nav-label">查看主机</span><span class="label label-info pull-right"></span></a>
|
||||
</li>
|
||||
<li id="jperm">
|
||||
<a><i class="fa fa-cube"></i> <span class="nav-label">权限申请</span><span class="fa arrow"></span></a>
|
||||
|
@ -144,4 +144,4 @@
|
|||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
|
@ -17,8 +17,13 @@
|
|||
<ul class="dropdown-menu animated fadeInRight m-t-xs">
|
||||
<li><a value="/juser/profile/?id={{ session_user_id }}" class="iframe_user">个人信息</a></li>
|
||||
<li><a href="/juser/change_info/">修改信息</a></li>
|
||||
<li><a href="/juser/change_role/">切换角色</a></li>
|
||||
|
||||
{% if not user.role == 'CU' %}
|
||||
{% if request.session.role_id == 0 %}
|
||||
<li><a href="/juser/change_role/">系统后台</a></li>
|
||||
{% else %}
|
||||
<li><a href="/juser/change_role/">主机控制台</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="divider"></li>
|
||||
<li><a href="/logout/">注销</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -1,72 +1,56 @@
|
|||
<div class="col-sm-6">
|
||||
<div class="dataTables_paginate paging_simple_numbers" id="editable_paginate">
|
||||
<ul class="pagination" style="margin-top: 0; float: right">
|
||||
{% if keyword %}
|
||||
{% if contacts.has_previous %}
|
||||
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="editable_previous">
|
||||
<a href="?keyword={{ keyword }}&page={{ contacts.previous_page_number }}">Previous</a>
|
||||
<a class="page" href="?page={{ contacts.previous_page_number }}">Previous</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="paginate_button previous disabled" aria-controls="editable" tabindex="0" id="editable_previous">
|
||||
<a href="#">Previous</a>
|
||||
<a class="page" href="#">Previous</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% ifequal show_first 1 %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page=1" title="第1页">1...</a></li>
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page=1" title="第1页">1...</a></li>
|
||||
{% endifequal %}
|
||||
{% for page in page_range %}
|
||||
{% ifequal current_page page %}
|
||||
<li class="paginate_button active" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
<li class="paginate_button active" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
{% else %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% ifequal show_end 1 %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li>
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li>
|
||||
{% endifequal %}
|
||||
{% if contacts.has_next %}
|
||||
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="editable_next">
|
||||
<a href="?keyword={{ keyword }}&page={{ contacts.next_page_number }}">Next</a>
|
||||
<a class="page" href="?page={{ contacts.next_page_number }}">Next</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="paginate_button next disabled" aria-controls="editable" tabindex="0" id="editable_next">
|
||||
<a href="#">Next</a>
|
||||
<a class="page" href="#">Next</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if contacts.has_previous %}
|
||||
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="editable_previous">
|
||||
<a href="?page={{ contacts.previous_page_number }}">Previous</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="paginate_button previous disabled" aria-controls="editable" tabindex="0" id="editable_previous">
|
||||
<a href="#">Previous</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% ifequal show_first 1 %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page=1" title="第1页">1...</a></li>
|
||||
{% endifequal %}
|
||||
{% for page in page_range %}
|
||||
{% ifequal current_page page %}
|
||||
<li class="paginate_button active" aria-controls="editable" tabindex="0"><a href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
{% else %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% ifequal show_end 1 %}
|
||||
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li>
|
||||
{% endifequal %}
|
||||
{% if contacts.has_next %}
|
||||
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="editable_next">
|
||||
<a href="?page={{ contacts.next_page_number }}">Next</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="paginate_button next disabled" aria-controls="editable" tabindex="0" id="editable_next">
|
||||
<a href="#">Next</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('.page').click(function(){
|
||||
var searchStr = location.search;
|
||||
var old_href = $(this).attr('href').replace('?', '');
|
||||
var searchArray = searchStr.split('&');
|
||||
|
||||
if (searchStr.indexOf('page')){
|
||||
searchArray.pop();
|
||||
}
|
||||
searchArray.push(old_href);
|
||||
|
||||
if (searchArray.length > 2){
|
||||
$(this).attr('href', searchArray.join('&'));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
@ -46,20 +46,28 @@
|
|||
{% 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_r.default_user }}" class="form-control">
|
||||
<input id="username" name="username" placeholder="Username" type="text" value="{{ setting_default.default_user }}" class="form-control">
|
||||
</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_r.default_port }}" class="form-control">
|
||||
<input id="port" name="port" placeholder="Port" type="text" value="{{ setting_default.default_port }}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="form-group">
|
||||
<label for="key" class="col-sm-2 control-label">默认密钥<span class="red-fonts">*</span></label>
|
||||
<label for="key" class="col-sm-2 control-label">默认密码</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="password" name="password" placeholder="Password" type="password" value="{{ setting_default.default_password }}" 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>
|
||||
</div>
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
.blue
|
||||
{
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
{#<script>#}
|
||||
{#$('html').ajaxSend(function(event, xhr, settings) {#}
|
||||
{# function getCookie(name) {#}
|
||||
{# var cookieValue = null;#}
|
||||
{# if (document.cookie && document.cookie != '') {#}
|
||||
{# var cookies = document.cookie.split(';');#}
|
||||
{# for (var i = 0; i < cookies.length; i++) {#}
|
||||
{# var cookie = jQuery.trim(cookies[i]);#}
|
||||
{# // Does this cookie string begin with the name we want?#}
|
||||
{# if (cookie.substring(0, name.length + 1) == (name + '=')) {#}
|
||||
{# cookieValue = decodeURIComponent(cookie.substring(name.length + 1));#}
|
||||
{# break;#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# return cookieValue;#}
|
||||
{# }#}
|
||||
{# if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {#}
|
||||
{# // Only send the token to relative URLs i.e. locally.#}
|
||||
{# xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));#}
|
||||
{# }#}
|
||||
{#});#}
|
||||
{#</script>#}
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$("#btn1").click(function(){
|
||||
$("#test").text(function(i,origText){
|
||||
return "Old text: " + origText + "New text: Hllo" + i
|
||||
})
|
||||
});
|
||||
|
||||
$("#btn2").click(function(){
|
||||
$.post('/test_ajax/',
|
||||
{'name': 'join', 'age': 10},
|
||||
function(data, status){
|
||||
$('#btn1').text(data)
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
$("#btn3").click(function(){
|
||||
$("p").toggleClass('blue')
|
||||
// $("p").addClass('blue')
|
||||
// $("p").before("Some thine")
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="test" class="blue">
|
||||
这是段落中的<b>粗体</b>文本。
|
||||
<p><span>hello</span></p>
|
||||
|
||||
</p>
|
||||
<!-- <input type="text" id="test2" name="nameaaaaaaaaaa" value="米老鼠"> -->
|
||||
<button id="btn1">显示文本</button>
|
||||
<button id="btn2">显示 HTML</button>
|
||||
<button id="btn3">显示 value</button>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,85 @@
|
|||
<html>
|
||||
<body>
|
||||
<div id="main" style="height:400px;"></div>
|
||||
...
|
||||
<script src="/static/js/echarts/echarts.js"></script>
|
||||
<script type="text/javascript">
|
||||
require.config({
|
||||
paths: {
|
||||
'echarts': '/static/js/echarts/chart',
|
||||
'echarts/chart/line': '/static/js/echarts/chart/line'
|
||||
}
|
||||
});
|
||||
require(
|
||||
[
|
||||
'echarts',
|
||||
'echarts/chart/line'
|
||||
],
|
||||
function (ec) {
|
||||
var myChart = ec.init(document.getElementById('main'));
|
||||
var option = {
|
||||
title : {
|
||||
text: '某楼盘销售情况',
|
||||
subtext: '纯属虚构'
|
||||
},
|
||||
tooltip : {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data:['意向','预购','成交']
|
||||
},
|
||||
toolbox: {
|
||||
show : true,
|
||||
feature : {
|
||||
mark : {show: true},
|
||||
dataView : {show: true, readOnly: false},
|
||||
magicType : {show: true, type: ['line', 'bar', 'stack', 'tiled']},
|
||||
restore : {show: true},
|
||||
saveAsImage : {show: true}
|
||||
}
|
||||
},
|
||||
calculable : true,
|
||||
xAxis : [
|
||||
{
|
||||
type : 'category',
|
||||
boundaryGap : false,
|
||||
data : ['周一','周二','周三','周四','周五','周六','周日']
|
||||
}
|
||||
],
|
||||
yAxis : [
|
||||
{
|
||||
type : 'value'
|
||||
}
|
||||
],
|
||||
series : [
|
||||
{
|
||||
name:'成交',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data:[10, 12, 21, 54, 260, 830, 710]
|
||||
},
|
||||
{
|
||||
name:'预购',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data:[30, 182, 434, 791, 390, 30, 10]
|
||||
},
|
||||
{
|
||||
name:'意向',
|
||||
type:'line',
|
||||
smooth:true,
|
||||
itemStyle: {normal: {areaStyle: {type: 'default'}}},
|
||||
data:[1320, 1132, 601, 234, 120, 90, 20]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
myChart.setOption(option);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue