merge master

pull/530/head
wangyong 2016-09-16 17:32:35 +08:00
commit 42012d7bfe
114 changed files with 6924 additions and 1102 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.DS_Store
*.pyc
*.pyo
*.swp
env
env*
dist

View File

@ -1,32 +1,37 @@
# ~*~ coding: utf-8 ~*~
from rest_framework import serializers
from .models import (
AssetGroup,Asset,IDC,AssetExtend
)
from rest_framework import viewsets,serializers
from rest_framework import viewsets, serializers
from .models import AssetGroup, Asset, IDC, AssetExtend
class AssetGroupSerializer(serializers.ModelSerializer):
class Meta:
model = AssetGroup
#exclude = [
#'password', 'first_name', 'last_name', 'secret_key_otp',
#'private_key', 'public_key', 'avatar',
#]
# exclude = [
# 'password', 'first_name', 'last_name', 'secret_key_otp',
# 'private_key', 'public_key', 'avatar',
# ]
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
#fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
class IDCSerializer(serializers.ModelSerializer):
class Meta:
model = IDC
#fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
class AssetGroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows AssetGroup to be viewed or edited.
""" API endpoint that allows AssetGroup to be viewed or edited.
some other comment
"""
queryset = AssetGroup.objects.all()
serializer_class = AssetGroupSerializer

View File

@ -1,7 +1,7 @@
# coding:utf-8
from django import forms
from .models import IDC, Asset, AssetGroup
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser
from django.utils.translation import gettext_lazy as _
@ -11,16 +11,17 @@ class AssetForm(forms.ModelForm):
fields = [
"ip", "other_ip", "remote_card_ip", "hostname", "port", "groups", "username", "password",
"idc", "mac_addr", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
"idc", "mac_address", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
"number", "status", "type", "env", "sn", "is_active", "comment"
]
widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join assetgroups')}),
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
}
class AssetGroupForm(forms.ModelForm):
# See AdminUserForm comment same it
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
@ -45,14 +46,169 @@ class AssetGroupForm(forms.ModelForm):
fields = [
"name", "comment"
]
help_texts = {
'name': '* required',
}
class IdcForm(forms.ModelForm):
class IDCForm(forms.ModelForm):
# See AdminUserForm comment same it
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
def __init__(self, *args, **kwargs):
if kwargs.get('instance'):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super(IDCForm, self).__init__(*args, **kwargs)
def _save_m2m(self):
super(IDCForm, self)._save_m2m()
assets = self.cleaned_data['assets']
self.instance.assets.clear()
self.instance.assets.add(*tuple(assets))
class Meta:
model = IDC
fields = ['name', "bandwidth", "operator", 'contact', 'phone', 'address', 'network', 'comment']
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Name'}),
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'network': forms.Textarea(
attrs={'placeholder': '192.168.1.0/24\n192.168.2.0/24'})
}
class AdminUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
auto_generate_key = forms.BooleanField(required=True, initial=True)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
def __init__(self, *args, **kwargs):
# When update a admin user instance, initial it
if kwargs.get('instance'):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super(AdminUserForm, self).__init__(*args, **kwargs)
def _save_m2m(self):
# Save assets relation with admin user
super(AdminUserForm, self)._save_m2m()
assets = self.cleaned_data['assets']
self.instance.assets.clear()
self.instance.assets.add(*tuple(assets))
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
admin_user = super(AdminUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
private_key_file = self.cleaned_data['private_key_file']
if password:
admin_user.password = password
print(password)
# Todo: Validate private key file, and generate public key
# Todo: Auto generate private key and public key
if private_key_file:
admin_user.private_key = private_key_file.read()
admin_user.save()
return self.instance
class Meta:
model = AdminUser
fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment']
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
help_texts = {
'name': '* required',
'username': '* required',
}
class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
asset_groups = forms.ModelMultipleChoiceField(queryset=AssetGroup.objects.all(),
label=_('Asset group'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')})
)
auto_generate_key = forms.BooleanField(initial=True)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
def __init__(self, *args, **kwargs):
# When update a admin user instance, initial it
if kwargs.get('instance'):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
initial['asset_groups'] = kwargs['instance'].asset_groups.all()
super(SystemUserForm, self).__init__(*args, **kwargs)
def _save_m2m(self):
# Save assets relation with admin user
super(SystemUserForm, self)._save_m2m()
assets = self.cleaned_data['assets']
asset_groups = self.cleaned_data['asset_groups']
self.instance.assets.clear()
self.instance.assets.add(*tuple(assets))
self.instance.asset_groups.clear()
self.instance.asset_groups.add(*tuple(asset_groups))
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
private_key_file = self.cleaned_data['private_key_file']
if password:
system_user.password = password
print(password)
# Todo: Validate private key file, and generate public key
# Todo: Auto generate private key and public key
if private_key_file:
system_user.private_key = private_key_file.read()
system_user.save()
return self.instance
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default',
'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
help_texts = {
'name': '* required',
'username': '* required',
'auth_push': 'Auto push system user to asset',
'auth_update': 'Auto update system user ssh key',
}

15
apps/assets/hands.py Normal file
View File

@ -0,0 +1,15 @@
"""
jumpserver.__app__.hands.py
~~~~~~~~~~~~~~~~~
This app depends other apps api, function .. should be import or write mack here.
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2016 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup

View File

@ -1,181 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-09-04 11:52
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AdminUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('username', models.CharField(max_length=16, verbose_name='Username')),
('password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', models.CharField(blank=True, max_length=4096, null=True, verbose_name='SSH private key')),
('is_default', models.BooleanField(default=False, verbose_name='As default')),
('auto_update', models.BooleanField(default=True, verbose_name='Auto update pass/key')),
('date_added', models.DateTimeField(auto_now=True, null=True, verbose_name='Date added')),
('create_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
],
options={
'db_table': 'adminuser',
},
),
migrations.CreateModel(
name='Asset',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip', models.CharField(max_length=32, verbose_name='IP')),
('other_ip', models.CharField(blank=True, max_length=255, verbose_name='Other IP')),
('remote_card_ip', models.CharField(blank=True, max_length=16, verbose_name='Remote card IP')),
('hostname', models.CharField(blank=True, max_length=128, unique=True, verbose_name='Hostname')),
('port', models.IntegerField(default=22, verbose_name='Port')),
('username', models.CharField(blank=True, max_length=16, verbose_name='Admin user')),
('password', models.CharField(blank=True, max_length=256, verbose_name='Admin password')),
('mac_addr', models.CharField(blank=True, max_length=20, verbose_name='Mac address')),
('brand', models.CharField(blank=True, max_length=64, verbose_name='Brand')),
('cpu', models.CharField(blank=True, max_length=64, verbose_name='CPU')),
('memory', models.CharField(blank=True, max_length=128, verbose_name='Memory')),
('disk', models.CharField(blank=True, max_length=1024, verbose_name='Disk')),
('os', models.CharField(blank=True, max_length=128, verbose_name='OS')),
('cabinet_no', models.CharField(blank=True, max_length=32, verbose_name='Cabinet number')),
('cabinet_pos', models.IntegerField(blank=True, verbose_name='Cabinet position')),
('number', models.CharField(blank=True, max_length=32, unique=True, verbose_name='Asset number')),
('sn', models.CharField(blank=True, max_length=128, unique=True, verbose_name='Serial number')),
('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('date_added', models.DateTimeField(auto_now=True, null=True, verbose_name='Date added')),
('comment', models.CharField(blank=True, max_length=128, verbose_name='Comment')),
('admin_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.AdminUser', verbose_name='Admin User')),
],
options={
'db_table': 'asset',
},
),
migrations.CreateModel(
name='AssetExtend',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.CharField(max_length=64, verbose_name='KEY')),
('value', models.CharField(max_length=64, verbose_name='VALUE')),
('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('date_added', models.DateTimeField(auto_now=True, null=True, verbose_name='Date added')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
],
options={
'db_table': 'assetextend',
},
),
migrations.CreateModel(
name='AssetGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, unique=True, verbose_name='Name')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
],
options={
'db_table': 'assetgroup',
},
),
migrations.CreateModel(
name='IDC',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=32, verbose_name='Name')),
('bandwidth', models.CharField(blank=True, max_length=32, verbose_name='Bandwidth')),
('contact', models.CharField(blank=True, max_length=16, verbose_name='Contact')),
('phone', models.CharField(blank=True, max_length=32, verbose_name='Phone')),
('address', models.CharField(blank=True, max_length=128, verbose_name='Address')),
('network', models.TextField(blank=True, verbose_name='Network')),
('date_added', models.DateField(auto_now=True, null=True, verbose_name='Date added')),
('operator', models.CharField(blank=True, max_length=32, verbose_name='Operator')),
('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
],
options={
'db_table': 'idc',
},
),
migrations.CreateModel(
name='Label',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.CharField(max_length=64, verbose_name='KEY')),
('value', models.CharField(max_length=64, verbose_name='VALUE')),
('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('date_added', models.DateTimeField(auto_now=True, null=True, verbose_name='Date added')),
('comment', models.CharField(blank=True, max_length=128, verbose_name='Comment')),
('asset', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.Asset', verbose_name='Asset')),
],
options={
'db_table': 'label',
},
),
migrations.CreateModel(
name='SysUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(max_length=16, verbose_name='Username')),
('password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('protocol', models.CharField(default='ssh', max_length=16, verbose_name='Protocol')),
('private_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH private key')),
('public_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH public key')),
('is_default', models.BooleanField(default=False, verbose_name='As default')),
('auto_push', models.BooleanField(default=True, verbose_name='Auto push')),
('auto_update', models.BooleanField(default=True, verbose_name='Auto update pass/key')),
('sudo', models.TextField(blank=True, max_length=4096, verbose_name='Sudo')),
('shell', models.CharField(blank=True, max_length=64, verbose_name='Shell')),
('home', models.CharField(blank=True, max_length=64, verbose_name='Home')),
('uid', models.IntegerField(blank=True, verbose_name='Uid')),
('date_added', models.DateTimeField(auto_now=True, null=True, verbose_name='Date added')),
('create_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
('comment', models.CharField(blank=True, max_length=128, verbose_name='Comment')),
],
options={
'db_table': 'sysuser',
},
),
migrations.AddField(
model_name='asset',
name='env',
field=models.ManyToManyField(related_name='asset_env_extend', to='assets.AssetExtend', verbose_name='Asset environment'),
),
migrations.AddField(
model_name='asset',
name='groups',
field=models.ManyToManyField(to='assets.AssetGroup', verbose_name='Asset groups'),
),
migrations.AddField(
model_name='asset',
name='idc',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.IDC', verbose_name='IDC'),
),
migrations.AddField(
model_name='asset',
name='status',
field=models.ManyToManyField(related_name='asset_status_extend', to='assets.AssetExtend', verbose_name='Asset status'),
),
migrations.AddField(
model_name='asset',
name='sys_user',
field=models.ManyToManyField(to='assets.SysUser', verbose_name='Sys User'),
),
migrations.AddField(
model_name='asset',
name='type',
field=models.ManyToManyField(related_name='asset_type_extend', to='assets.AssetExtend', verbose_name='Asset type'),
),
]

View File

@ -2,8 +2,11 @@
from __future__ import unicode_literals, absolute_import
from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
from common.utils import encrypt, decrypt
class AssetGroup(models.Model):
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
@ -16,6 +19,8 @@ class AssetGroup(models.Model):
class Meta:
db_table = 'asset_group'
logger = logging.getLogger(__name__)
class IDC(models.Model):
name = models.CharField(max_length=32, verbose_name=_('Name'))
@ -24,7 +29,7 @@ class IDC(models.Model):
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
address = models.CharField(max_length=128, blank=True, verbose_name=_("Address"))
network = models.TextField(blank=True, verbose_name=_('Network'))
date_added = models.DateField(auto_now=True, null=True, verbose_name=_('Date added'))
date_created = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
operator = models.CharField(max_length=32, blank=True, verbose_name=_('Operator'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
@ -35,87 +40,253 @@ class IDC(models.Model):
class Meta:
db_table = 'idc'
@classmethod
def generate_fake(cls, count=100):
from random import seed, choice
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
idc = cls(name=forgery_py.name.full_name(),
bandwidth='200M',
contact=forgery_py.name.full_name(),
phone=forgery_py.address.phone(),
address=forgery_py.address.city() + forgery_py.address.street_address(),
network="192.168.1.10/24\n192.168.1.20",
operator=choice(['北京联通', '北京电信', 'BGP全网通']),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
idc.save()
logger.debug('Generate fake asset group: %s' % idc.name)
except IntegrityError:
print('Error continue')
continue
class AssetExtend(models.Model):
key = models.CharField(max_length=64, verbose_name=_('KEY'))
value = models.CharField(max_length=64, verbose_name=_('VALUE'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_("Created by"))
date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
date_created = models.DateTimeField(auto_now=True, null=True, blank=True)
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
return '%(key)s: %(value)s' % {'key': self.key, 'value': self.value}
@classmethod
def initial(cls):
for k, v in (
(_('status'), _('In use')),
(_('status'), _('Out of use')),
(_('type'), _('Server')),
(_('type'), _('VM')),
(_('type'), _('Switch')),
(_('type'), _('Router')),
(_('type'), _('Firewall')),
(_('type'), _('Storage')),
(_('env'), _('Production')),
(_('env'), _('Development')),
(_('env'), _('Testing')),
):
cls.objects.create(key=k, value=v, created_by='System')
class Meta:
db_table = 'assetextend'
db_table = 'asset_extend'
class AdminUser(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_('Password'))
private_key = models.CharField(max_length=4096, null=True, blank=True, verbose_name=_('SSH private key'))
is_default = models.BooleanField(default=False, verbose_name=_('As default'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
def __unicode__(self):
return self.name
@property
def password(self):
return decrypt(self._password)
@password.setter
def password(self, password_raw):
self._password = encrypt(password_raw)
@property
def private_key(self):
return decrypt(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = encrypt(private_key_raw)
@property
def public_key(self):
return decrypt(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = encrypt(public_key_raw)
class Meta:
db_table = 'admin_user'
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
obj = cls(name=forgery_py.name.full_name(),
username=forgery_py.internet.user_name(),
password=forgery_py.lorem_ipsum.word(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
obj.save()
logger.debug('Generate fake asset group: %s' % obj.name)
except IntegrityError:
print('Error continue')
continue
class SystemUser(models.Model):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
create_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
home = models.CharField(max_length=64, blank=True, verbose_name=_('Home'))
uid = models.IntegerField(null=True, blank=True, verbose_name=_('Uid'))
date_created = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
@property
def password(self):
return decrypt(self._password)
@password.setter
def password(self, password_raw):
self._password = encrypt(password_raw)
@property
def private_key(self):
return decrypt(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = encrypt(private_key_raw)
@property
def public_key(self):
return decrypt(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = encrypt(public_key_raw)
class Meta:
db_table = 'system_user'
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
obj = cls(name=forgery_py.name.full_name(),
username=forgery_py.internet.user_name(),
password=forgery_py.lorem_ipsum.word(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
obj.save()
logger.debug('Generate fake asset group: %s' % obj.name)
except IntegrityError:
print('Error continue')
continue
class AssetGroup(models.Model):
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
system_users = models.ManyToManyField(SystemUser, related_name='asset_groups', blank=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
class Meta:
db_table = 'adminuser'
db_table = 'asset_group'
@classmethod
def initial(cls):
asset_group = cls(name=_('Default'), commont=_('Default asset group'))
asset_group.save()
class SysUser(models.Model):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
('telnet', 'telnet'),
)
name = models.CharField(max_length=128, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_('Password'))
protocol = models.CharField(max_length=16, default='ssh', verbose_name=_('Protocol'))
private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
is_default = models.BooleanField(default=False, verbose_name=_('As default'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
sudo = models.TextField(max_length=4096, blank=True, verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, blank=True, verbose_name=_('Shell'))
home = models.CharField(max_length=64, blank=True, verbose_name=_('Home'))
uid = models.IntegerField(blank=True, verbose_name=_('Uid'))
date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
create_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
def __unicode__(self):
return self.name
class Meta:
db_table = 'sysuser'
seed()
for i in range(count):
group = cls(name=forgery_py.name.full_name(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
group.save()
logger.debug('Generate fake asset group: %s' % group.name)
except IntegrityError:
print('Error continue')
continue
class Asset(models.Model):
ip = models.CharField(max_length=32, verbose_name=_('IP'))
other_ip = models.CharField(max_length=255, blank=True, verbose_name=_('Other IP'))
remote_card_ip = models.CharField(max_length=16, blank=True, verbose_name=_('Remote card IP'))
hostname = models.CharField(max_length=128, unique=True, blank=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, blank=True, verbose_name=_('Asset groups'))
username = models.CharField(max_length=16, blank=True, verbose_name=_('Admin user'))
password = models.CharField(max_length=256, blank=True, verbose_name=_("Admin password"))
admin_user = models.ForeignKey(AdminUser, null=True, on_delete=models.SET_NULL, verbose_name=_("Admin User"))
sys_user = models.ManyToManyField(SysUser, verbose_name=_("Sys User"))
idc = models.ForeignKey(IDC, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_('IDC'))
mac_addr = models.CharField(max_length=20, blank=True, verbose_name=_("Mac address"))
brand = models.CharField(max_length=64, blank=True, verbose_name=_('Brand'))
cpu = models.CharField(max_length=64, blank=True, verbose_name=_('CPU'))
memory = models.CharField(max_length=128, blank=True, verbose_name=_('Memory'))
disk = models.CharField(max_length=1024, blank=True, verbose_name=_('Disk'))
os = models.CharField(max_length=128, blank=True, verbose_name=_('OS'))
cabinet_no = models.CharField(max_length=32, blank=True, verbose_name=_('Cabinet number'))
ip = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('IP'))
other_ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Other IP'))
remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote card IP'))
hostname = models.CharField(max_length=128, unique=True, null=True, blank=True, verbose_name=_('Hostname'))
port = models.IntegerField(null=True, blank=True, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, related_name='assets', verbose_name=_('Asset groups'))
username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Admin user'))
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_("Admin password"))
admin_user = models.ForeignKey(AdminUser, null=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
system_user = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
idc = models.ForeignKey(IDC, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'))
mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand'))
cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU'))
memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Memory'))
disk = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number'))
cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
number = models.CharField(max_length=32, blank=True, verbose_name=_('Asset number'))
status = models.ForeignKey(AssetExtend, null=True, blank=True,
@ -127,11 +298,11 @@ class Asset(models.Model):
sn = models.CharField(max_length=128, blank=True, verbose_name=_('Serial number'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now=True, null=True, blank=True, verbose_name=_('Date added'))
comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return '%(ip)s:%(port)d' % {'ip': self.ip, 'port': self.port}
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
def initial(self):
pass
@ -140,13 +311,32 @@ class Asset(models.Model):
db_table = 'asset'
index_together = ('ip', 'port')
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
asset = cls(ip='%s.%s.%s.%s' % tuple([forgery_py.forgery.basic.text(length=3, digits=True)
for i in range(0, 4)]),
port=22,
created_by='Fake')
try:
asset.save()
logger.debug('Generate fake asset : %s' % asset.ip)
except IntegrityError:
print('Error continue')
continue
class Label(models.Model):
key = models.CharField(max_length=64, verbose_name=_('KEY'))
value = models.CharField(max_length=64, verbose_name=_('VALUE'))
asset = models.ForeignKey(Asset, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_('Asset'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_("Created by"))
date_added = models.DateTimeField(auto_now=True, null=True, verbose_name=_('Date added'))
date_created = models.DateTimeField(auto_now=True, null=True)
comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
@ -155,3 +345,7 @@ class Label(models.Model):
class Meta:
db_table = 'label'
def generate_fake():
for cls in (Asset, AssetGroup, IDC):
cls.generate_fake()

View File

@ -0,0 +1,70 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create admin user' %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<div class="col-sm-8">
{{ form.auto_generate_key}}
</div>
</div>
{{ form.password|bootstrap_horizontal }}
{{ form.private_key_file|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
<div class="col-sm-8">
{{ form.as_default}}
</div>
</div>
{{ form.assets|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
})
</script>
{% endblock %}

View File

@ -0,0 +1,220 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ admin_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#"></a>
</li>
<li><a href="#"></a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ admin_user.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Username' %}:</td>
<td><b>{{ admin_user.username }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ admin_user.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ asset_group.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ admin_user.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#"></a>
</li>
<li><a href="#"></a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Alive' %}</th>
</tr>
</thead>
<tbody>
{% for asset in page_obj %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>{{ asset.port }}</td>
<td>Alive</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Get install script' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
</span>
</td>
</tr>
<tr>
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
</span>
</td>
</tr>
<tr>
<td width="50%">{% trans 'Reset private key' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Replace asset admin user with this' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-info btn-sm">{% trans 'Replace' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'assets:admin-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create admin user" %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=username">{% trans 'Username' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Lost connection' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center"></th>
{% endblock %}
{% block table_body %}
{% for admin_user in admin_user_list %}
<tr class="gradeX">
<td class="text-center">{{ admin_user.id }}</td>
<td>
<a href="{% url 'assets:admin-user-detail' pk=admin_user.id %}">
{{ admin_user.name }}
</a>
</td>
<td class="text-center">{{ admin_user.username }}</td>
<td class="text-center">{{ admin_user.assets.count }}</td>
<td class="text-center">{{ admin_user.assets.count }}</td>
<td class="text-center">{{ admin_user.comment|truncatewords:8 }}</td>
<td class="text-center">
<!-- Todo: Click script button will paste a url to clipboard like: curl http://url/admin_user_create.sh | bash -->
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-primary">{% trans 'Script' %}</a>
<!-- Todo: Click refresh button will run a task to test admin user could connect asset or not immediately -->
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-warning">{% trans 'Refresh' %}</a>
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="obj_del(this,'{{ admin_user.name }}','{% url 'assets:admin-user-delete' admin_user.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}

View File

@ -151,7 +151,7 @@
</tr>
<tr>
<td class="text-navy">添加日期</td>
<td>{{ asset.date_added|date:"Y-m-d H:i:s" }}</td>
<td>{{ asset.date_created|date:"Y-m-d H:i:s" }}</td>
</tr>
<tr>
<td class="text-navy">备注</td>

View File

@ -27,22 +27,10 @@
</div>
</div>
<div class="ibox-content">
<form method="post" id="userForm" class="form-horizontal" action="" >
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.assets|bootstrap_horizontal }}
{# <div class="form-group">#}
{# <label for="users" class="col-sm-2 control-label">{% trans 'Asset' %}</label>#}
{# <div class="col-sm-9">#}
{# <select name="assets" id="assets" data-placeholder="{% trans 'Select asset' %}" class="select2 form-control m-b" multiple tabindex="2">#}
{# {% for asset in assets %}#}
{# <option value="{{ asset.id }}">{{ asset.ip }}:{{ asset.port }}</option>#}
{# {% endfor %}#}
{# </select>#}
{# </div>#}
{# </div>#}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">

View File

@ -0,0 +1,223 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset group perm' %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ asset_group.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ asset_group.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ asset_group.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ asset_group.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ asset_group.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left"></span>{% trans 'Asset list of ' %} <b>{{ asset_group.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Alive' %}</th>
</tr>
</thead>
<tbody>
{% for asset in page_obj %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>{{ asset.port }}</td>
<td>Alive</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Associate system user' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'repush system user' %}:</td>
<td>
<span style="float: right">
<button class="btn btn-danger btn-xs" type="button"><i class="fa fa-refresh"></i></button>
</span>
</td>
</tr>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select system user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm">{% trans 'Associate' %}</button>
</td>
</tr>
</form>
{% for group in user.groups.all %}
<tr>
<td ><b>{{ group.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset to this group' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select asset user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-info btn-sm">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for group in user.groups.all %}
<tr>
<td ><b>{{ group.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
})
</script>
{% endblock %}

View File

@ -27,10 +27,10 @@
</a>
</td>
<td class="text-center">{{ asset_group.assets.count }}</td>
<td class="text-center">{{ asset_group.comment }}</td>
<td class="text-center">{{ asset_group.comment|truncatewords:8 }}</td>
<td class="text-center">
<a href="{% url 'assets:asset-group-update' pk=asset_group.id %}" class="btn btn-xs btn-info">{% trans 'Edit' %}</a>
<a href="{% url 'assets:asset-group-delete' pk=asset_group.id %}" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
<a href="{% url 'assets:asset-group-update' pk=asset_group.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="obj_del(this,'{{ asset_group.name }}','{% url 'assets:asset-group-delete' asset_group.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}

View File

@ -0,0 +1,15 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans 'Confirm delete' %}</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object.name }}"?</p>
<input type="submit" value="Confirm" />
</form>
</body>
</html>

View File

@ -0,0 +1,61 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create idc' %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.assets|bootstrap_horizontal }}
{{ form.bandwidth|bootstrap_horizontal }}
{{ form.operator|bootstrap_horizontal }}
{{ form.contact|bootstrap_horizontal }}
{{ form.phone|bootstrap_horizontal }}
{{ form.address|bootstrap_horizontal }}
{{ form.network|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
})
</script>
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'assets:idc-create' %}" class="btn btn-sm btn-primary "> {% trans "Create IDC" %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Bandwidth' %}</th>
<th class="text-center">{% trans 'Contact' %}</th>
<th class="text-center">{% trans 'Phone' %}</th>
<th class="text-center">{% trans 'Address' %}</th>
<th class="text-center"></th>
{% endblock %}
{% block table_body %}
{% for idc in idc_list %}
<tr class="gradeX">
<td class="text-center">{{ idc.id }}</td>
<td class="text-center">{{ idc.name }}</td>
<td class="text-center">{{ idc.assets.count }}</td>
<td class="text-center">{{ idc.bandwidth }}</td>
<td class="text-center">{{ idc.contact }}</td>
<td class="text-center">{{ idc.phone }}</td>
<td class="text-center">{{ idc.address }}</td>
<td class="text-center">
<a href="{% url 'assets:idc-update' pk=idc.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="objectDelete(this, '{{ idc.name }}', '{% url 'assets:idc-delete' idc.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}
{% block custom_foot_js %}
<script>
//删除提示样式二,可以选用。
function obj_del2(obj,name,url){
//alert(local);
top.$.jBox.confirm("您确定要删除【"+name+"】吗?", "系统提示",function (c, d, e) {
if (c == "ok") {
//loading("正在提交,请稍等...");
$.ajax({
type : "post",
url : url,
data : {
// idc_id : idc_id
},
success : function(data) {
// alert('删除成功!')
$(obj).parent().parent().remove();
},
dataType : "text"
});
}
}, {buttonsFocus: 1,
draggable: false,
showClose: false});
top.$(".jbox-body .jbox-icon").css("top", "35px");
top.$(".jbox-button-panel").css("height", "35px");
return false
}
</script>
{% endblock %}

View File

@ -0,0 +1,135 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active"><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a>
</li>
<li><a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Alive' %}</th>
</tr>
</thead>
<tbody>
{% for asset in page_obj %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>{{ asset.port }}</td>
<td>Alive</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset to this system user' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,136 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a>
</li>
<li class="active">
<a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset num' %}</th>
<th>{% trans 'Unavailable num' %}</th>
<th>{% trans 'Comment' %}</th>
</tr>
</thead>
<tbody>
{% for asset_group in page_obj %}
<tr>
<td>{{ asset_group.name }}</td>
<td>{{ asset_group_group.assets.count }}</td>
<td>{{ asset_group_group.assets.count }}</td>
<td>{{ asset_group.comment|truncatewords:4 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset group to this system user' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select asset group' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset_group in asset_groups %}
<option value="{{ asset_group.id }}">{{ asset_group.name}}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,95 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create system user' %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
{{ form.protocol|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<div class="col-sm-8">
{{ form.auto_generate_key}}
</div>
</div>
{{ form.password|bootstrap_horizontal }}
{{ form.private_key_file|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
<div class="col-sm-8">
{{ form.as_default}}
</div>
</div>
<div class="form-group">
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
<div class="col-sm-8">
{{ form.auto_push}}
</div>
</div>
<div class="form-group">
<label for="{{ form.as_update.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto update' %}</label>
<div class="col-sm-8">
{{ form.auto_update}}
</div>
</div>
{{ form.assets|bootstrap_horizontal }}
{{ form.asset_groups|bootstrap_horizontal }}
{{ form.sudo|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
{{ form.home|bootstrap_horizontal }}
{{ form.shell|bootstrap_horizontal }}
{{ form.uid|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') {
$('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove();
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').remove();
$('#'+'{{ form.auto_push.id_for_label }}').closest('.form-group').remove();
$('#'+'{{ form.auto_update.id_for_label }}').closest('.form-group').remove();
}
})
</script>
{% endblock %}

View File

@ -0,0 +1,188 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}
</a>
</li>
<li>
<a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ system_user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ system_user.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Username' %}:</td>
<td><b>{{ system_user.username }}</b></td>
</tr>
<tr>
<td>{% trans 'Protocol' %}:</td>
<td><b>{{ system_user.protocol }}</b></td>
</tr>
<tr>
<td>{% trans 'Auto push' %}:</td>
<td><b>{{ system_user.protocol }}</b></td>
</tr>
<tr>
<td>{% trans 'Auto update' %}:</td>
<td><b>{{ system_user.auto_update }}</b></td>
</tr>
<tr>
<td>{% trans 'As default' %}:</td>
<td><b>{{ system_user.protocol }}</b></td>
</tr>
<tr>
<td>{% trans 'Sudo' %}:</td>
<td><b>{{ system_user.sudo }}</b></td>
</tr>
{% if system_user.shell %}
<tr>
<td>{% trans 'Shell' %}:</td>
<td><b>{{ system_user.shell }}</b></td>
</tr>
{% endif %}
{% if system_user.home %}
<tr>
<td>{% trans 'Home' %}:</td>
<td><b>{{ system_user.home }}</b></td>
</tr>
{% endif %}
{% if system_user.uid %}
<tr>
<td>{% trans 'Uid' %}:</td>
<td><b>{{ system_user.uid }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ system_user.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ asset_group.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ system_user.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Get mannual install script' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
</span>
</td>
</tr>
<tr>
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
</span>
</td>
</tr>
<tr>
<td width="50%">{% trans 'Reset private key' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'assets:system-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create system user" %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center"><a href="{% url 'assets:system-user-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center"><a href="{% url 'assets:system-user-list' %}?sort=username">{% trans 'Username' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Asset group num' %}</th>
<th class="text-center">{% trans 'Unavailable' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center"></th>
{% endblock %}
{% block table_body %}
{% for system_user in system_user_list %}
<tr class="gradeX">
<td class="text-center">{{ system_user.id }}</td>
<td>
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}">
{{ system_user.name }}
</a>
</td>
<td class="text-center">{{ system_user.username }}</td>
<td class="text-center">{{ system_user.assets.count }}</td>
<td class="text-center">{{ system_user.asset_groups.count }}</td>
<td class="text-center">{{ system_user.assets.count }}</td>
<td class="text-center">{{ system_user.comment|truncatewords:4 }}</td>
<td class="text-center">
<!-- Todo: Click script button will paste a url to clipboard like: curl http://url/system_user_create.sh | bash -->
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-primary">{% trans 'Script' %}</a>
<!-- Todo: Click refresh button will run a task to test admin user could connect asset or not immediately -->
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-warning">{% trans 'Refresh' %}</a>
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="obj_del(this,'{{ system_user.name }}','{% url 'assets:system-user-delete' system_user.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}

View File

@ -12,16 +12,43 @@ import views
app_name = 'assets'
urlpatterns = [
# Resource asset url
url(r'^$', views.AssetListView.as_view(), name='asset-index'),
url(r'^asset$', views.AssetListView.as_view(), name='asset-list'),
url(r'^asset/create$', views.AssetCreateView.as_view(), name='asset-create'),
url(r'^asset/(?P<pk>[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset/(?P<pk>[0-9]+)$/update', views.AssetUpdateView.as_view(), name='asset-update'),
url(r'^asset/(?P<pk>[0-9]+)/update', views.AssetUpdateView.as_view(), name='asset-update'),
url(r'^asset/(?P<pk>[0-9]+)/delete$', views.AssetDeleteView.as_view(), name='asset-delete'),
# Resource asset group url
url(r'^asset-group$', views.AssetGroupListView.as_view(), name='asset-group-list'),
url(r'^asset-group/create$', views.AssetGroupCreateView.as_view(), name='asset-group-create'),
url(r'^asset-group/(?P<pk>[0-9]+)$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
url(r'^asset-group/(?P<pk>[0-9]+)/update$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
url(r'^asset-group/(?P<pk>[0-9]+)/delete$', views.AssetGroupDeleteView.as_view(), name='asset-group-delete'),
# Resource idc url
url(r'^idc$', views.IDCListView.as_view(), name='idc-list'),
url(r'^idc/create$', views.IDCCreateView.as_view(), name='idc-create'),
url(r'^idc/(?P<pk>[0-9]+)$', views.IDCDetailView.as_view(), name='idc-detail'),
url(r'^idc/(?P<pk>[0-9]+)/update', views.IDCUpdateView.as_view(), name='idc-update'),
url(r'^idc/(?P<pk>[0-9]+)/delete$', views.IDCDeleteView.as_view(), name='idc-delete'),
# Resource admin user url
url(r'^admin-user$', views.AdminUserListView.as_view(), name='admin-user-list'),
url(r'^admin-user/create$', views.AdminUserCreateView.as_view(), name='admin-user-create'),
url(r'^admin-user/(?P<pk>[0-9]+)$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
url(r'^admin-user/(?P<pk>[0-9]+)/update', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
url(r'^admin-user/(?P<pk>[0-9]+)/delete$', views.AdminUserDeleteView.as_view(), name='admin-user-delete'),
# Resource system user url
url(r'^system-user$', views.SystemUserListView.as_view(), name='system-user-list'),
url(r'^system-user/create$', views.SystemUserCreateView.as_view(), name='system-user-create'),
url(r'^system-user/(?P<pk>[0-9]+)$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
url(r'^system-user/(?P<pk>[0-9]+)/update', views.SystemUserUpdateView.as_view(), name='system-user-update'),
url(r'^system-user/(?P<pk>[0-9]+)/delete$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
url(r'^system-user/(?P<pk>[0-9]+)/asset$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
url(r'^system-user/(?P<pk>[0-9]+)/asset-group$', views.SystemUserAssetGroupView.as_view(),
name='system-user-asset-group'),
# url(r'^api/v1.0/', include(router.urls)),
]

View File

@ -1,23 +1,4 @@
# ~*~ coding: utf-8 ~*~
#
from django.contrib.auth.mixins import UserPassesTestMixin
from django.urls import reverse_lazy
from common.tasks import send_mail_async
from common.utils import reverse
from users.models import User
try:
import cStringIO as StringIO
except ImportError:
import StringIO
class AdminUserRequiredMixin(UserPassesTestMixin):
login_url = reverse_lazy('users:login')
def test_func(self):
return self.request.user.is_staff

View File

@ -2,18 +2,17 @@
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext as _
from django.shortcuts import get_object_or_404
from django.views.generic import TemplateView, ListView
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.conf import settings
from django.db.models import Q
from django.views.generic import TemplateView, ListView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from .models import Asset, AssetGroup, IDC, AssetExtend
from .forms import AssetForm, AssetGroupForm
from .utils import AdminUserRequiredMixin
from .models import Asset, AssetGroup, IDC, AssetExtend, AdminUser, SystemUser
from .forms import AssetForm, AssetGroupForm, IDCForm, AdminUserForm, SystemUserForm
from .hands import AdminUserRequiredMixin
class AssetCreateView(CreateView):
@ -50,12 +49,14 @@ class AssetDetailView(DetailView):
template_name = 'assets/asset_detail.html'
class AssetGroupCreateView(CreateView):
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
model = AssetGroup
form_class = AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
# Todo: Asset group create template select assets so hard, need be resolve next
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
@ -70,25 +71,57 @@ class AssetGroupCreateView(CreateView):
return super(AssetGroupCreateView, self).form_valid(form)
class AssetGroupListView(ListView):
class AssetGroupListView(AdminUserRequiredMixin, ListView):
model = AssetGroup
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'asset_group_list'
template_name = 'assets/asset_group_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Asset group list')
'action': _('Asset group list'),
'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AssetGroupListView, self).get_context_data(**kwargs)
def get_queryset(self):
self.queryset = super(AssetGroupListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
class AssetGroupDetailView(DetailView):
pass
if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword))
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
class AssetGroupUpdateView(UpdateView):
class AssetGroupDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
template_name = 'assets/asset_group_detail.html'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetGroup.objects.all())
return super(AssetGroupDetailView, self).get(request, *args, **kwargs)
def get_queryset(self):
return self.object.assets.all()
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Asset group detail'),
'asset_group': self.object,
}
kwargs.update(context)
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetGroup
form_class = AssetGroupForm
template_name = 'assets/asset_group_create.html'
@ -104,5 +137,313 @@ class AssetGroupUpdateView(UpdateView):
return super(AssetGroupUpdateView, self).get_context_data(**kwargs)
class AssetGroupDeleteView(DeleteView):
class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
template_name = 'assets/delete_confirm.html'
model = AssetGroup
success_url = reverse_lazy('assets:asset-group-list')
class IDCListView(AdminUserRequiredMixin, ListView):
model = IDC
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'idc_list'
template_name = 'assets/idc_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('IDC list'),
'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(IDCListView, self).get_context_data(**kwargs)
def get_queryset(self):
self.queryset = super(IDCListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword))
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
class IDCCreateView(AdminUserRequiredMixin, CreateView):
model = IDC
form_class = IDCForm
template_name = 'assets/idc_create_update.html'
success_url = reverse_lazy('assets:idc-list')
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'Create IDC'
}
kwargs.update(context)
return super(IDCCreateView, self).get_context_data(**kwargs)
class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
model = IDC
form_class = IDCForm
template_name = 'assets/idc_create_update.html'
context_object_name = 'idc'
success_url = reverse_lazy('assets:idc-list')
def form_valid(self, form):
idc = form.save(commit=False)
idc.save()
return super(IDCUpdateView, self).form_valid(form)
class IDCDetailView(AdminUserRequiredMixin, DetailView):
pass
class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
model = IDC
template_name = 'assets/delete_confirm.html'
success_url = reverse_lazy('assets:idc-list')
class AdminUserListView(AdminUserRequiredMixin, ListView):
model = AdminUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'admin_user_list'
template_name = 'assets/admin_user_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Admin user list'),
'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs)
def get_queryset(self):
# Todo: Default order by lose asset connection num
self.queryset = super(AdminUserListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword))
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AdminUser
form_class = AdminUserForm
template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list')
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'Create admin user'
}
kwargs.update(context)
return super(AdminUserCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _('Create admin user <a href="%s">%s</a> successfully.' %
(
reverse_lazy('assets:admin-user-detail', kwargs={'pk': self.object.pk}),
self.object.name,
))
return success_message
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser
form_class = AdminUserForm
template_name = 'assets/admin_user_create_update.html'
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'Update admin user'
}
kwargs.update(context)
return super(AdminUserUpdateView, self).get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail', pk=self.object.pk)
return success_url
class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'assets/admin_user_detail.html'
context_object_name = 'admin_user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AdminUser.objects.all())
return super(AdminUserDetailView, self).get(request, *args, **kwargs)
# Todo: queryset default order by connectivity, need ops support
def get_queryset(self):
return self.object.assets.all()
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'Admin user detail'
}
kwargs.update(context)
return super(AdminUserDetailView, self).get_context_data(**kwargs)
class AdminUserDeleteView(AdminUserRequiredMixin, DeleteView):
model = AdminUser
template_name = 'assets/delete_confirm.html'
success_url = reverse_lazy('assets:admin-user-list')
class SystemUserListView(AdminUserRequiredMixin, ListView):
model = SystemUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'system_user_list'
template_name = 'assets/system_user_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('System user list'),
'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(SystemUserListView, self).get_context_data(**kwargs)
def get_queryset(self):
# Todo: Default order by lose asset connection num
self.queryset = super(SystemUserListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword))
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = SystemUser
form_class = SystemUserForm
template_name = 'assets/system_user_create_update.html'
success_url = reverse_lazy('assets:system-user-list')
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create system user'),
}
kwargs.update(context)
return super(SystemUserCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _('Create system user <a href="%s">%s</a> successfully.' %
(
reverse_lazy('assets:system-user-detail', kwargs={'pk': self.object.pk}),
self.object.name,
))
return self.success_message
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = SystemUser
form_class = SystemUserForm
template_name = 'assets/system_user_create_update.html'
success_message = _('Update system user <a href="%s">%s</a> successfully.')
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Update system user')
}
kwargs.update(context)
return super(SystemUserUpdateView, self).get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:system-user-detail', pk=self.object.pk)
return success_url
class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'assets/system_user_detail.html'
context_object_name = 'system_user'
model = SystemUser
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('System user detail')
}
kwargs.update(context)
return super(SystemUserDetailView, self).get_context_data(**kwargs)
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
model = SystemUser
template_name = 'assets/delete_confirm.html'
success_url = reverse_lazy('assets:system-user-list')
class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'assets/system_user_asset.html'
context_object_name = 'system_user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=SystemUser.objects.all())
return super(SystemUserAssetView, self).get(request, *args, **kwargs)
# Todo: queryset default order by connectivity, need ops support
def get_queryset(self):
return self.object.assets.all()
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'System user asset',
'assets': self.get_queryset(),
}
kwargs.update(context)
return super(SystemUserAssetView, self).get_context_data(**kwargs)
class SystemUserAssetGroupView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
template_name = 'assets/system_user_asset_group.html'
context_object_name = 'system_user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=SystemUser.objects.all())
return super(SystemUserAssetGroupView, self).get(request, *args, **kwargs)
# Todo: queryset default order by connectivity, need ops support
def get_queryset(self):
return self.object.asset_groups.all()
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'System user asset group',
'asset_groups': self.get_queryset(),
}
kwargs.update(context)
return super(SystemUserAssetGroupView, self).get_context_data(**kwargs)

View File

@ -1,3 +1,18 @@
# Common app
Common app provide common view, function or others.
Common app shouldn't rely on other apps, because It may lead to cycle
import.
If your want to implement some function or class, you should think
whether other app use or not. If yes, You should make in common.
If the ability more relate to your app tightness, It's mean your app
provide this ability, not common, You should write it on your app utils.
## Celery usage

View File

@ -2,9 +2,15 @@
#
from __future__ import unicode_literals
from six import string_types
from itertools import chain
import string
import logging
from django.shortcuts import reverse as dj_reverse
from django.conf import settings
from django.core import signing
from django.utils import timezone
def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None, external=False):
@ -21,3 +27,76 @@ def get_object_or_none(model, **kwargs):
except model.DoesNotExist:
obj = None
return obj
def encrypt(*args, **kwargs):
return signing.dumps(*args, **kwargs)
def decrypt(*args, **kwargs):
return signing.loads(*args, **kwargs)
def date_expired_default():
try:
years = int(settings.CONFIG.DEFAULT_EXPIRED_YEARS)
except TypeError:
years = 70
return timezone.now() + timezone.timedelta(days=365 * years)
def combine_seq(s1, s2, callback=None):
for s in (s1, s2):
if not hasattr(s, '__iter__'):
return []
seq = chain(s1, s2)
if callback:
seq = map(callback, seq)
return seq
def search_object_attr(obj, value='', attr_list=None, ignore_case=False):
"""It's provide a method to search a object attribute equal some value
If object some attribute equal :param: value, return True else return False
class A():
name = 'admin'
age = 7
:param obj: A object
:param value: A string match object attribute
:param attr_list: Only match attribute in attr_list
:param ignore_case: Ignore case
:return: Boolean
"""
if value == '':
return True
try:
object_attr = obj.__dict__
except AttributeError:
return False
if attr_list is not None:
new_object_attr = {}
for attr in attr_list:
new_object_attr[attr] = object_attr.pop(attr)
object_attr = new_object_attr
if ignore_case:
if not isinstance(value, string_types):
return False
if value.lower() in map(string.lower, map(str, object_attr.values())):
return True
else:
if value in object_attr.values():
return True
return False
def get_logger(name=None):
return logging.getLogger('jumpserver.%s' % name)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[{"model": "users.usergroup", "pk": 1, "fields": {"name": "Default", "comment": "Default user group for all user", "date_added": "2016-09-05T11:39:25.770Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$5ReHkQOQA2Hk$DIW0b5U/uK+U0xqjA3QpYvBcODNhm2MPCm7YWbQys3I=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2016-09-05T11:39:25.771Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "is_first_login": false, "date_expired": "2086-08-19T11:39:25.771Z", "created_by": "System", "user_permissions": [], "groups": [1]}}]
[{"model": "users.usergroup", "pk": 1, "fields": {"name": "Default", "comment": "Default user group for all user", "date_created": "2016-09-05T11:39:25.770Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$5ReHkQOQA2Hk$DIW0b5U/uK+U0xqjA3QpYvBcODNhm2MPCm7YWbQys3I=", "last_login": null, "first_name": "", "last_name": "", "is_active": true, "date_joined": "2016-09-05T11:39:25.771Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "role": "Admin", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "_private_key": "", "_public_key": "", "comment": "Administrator is the super user of system", "is_first_login": false, "date_expired": "2086-08-19T11:39:25.771Z", "created_by": "System", "user_permissions": [], "groups": [1]}}]

View File

@ -108,6 +108,7 @@ TEMPLATES = [
# WSGI_APPLICATION = 'jumpserver.wsgi.application'
LOGIN_REDIRECT_URL = reverse_lazy('index')
LOGIN_URL = reverse_lazy('users:login')
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
@ -227,7 +228,7 @@ USE_L10N = True
USE_TZ = True
# I18N translation
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'),]
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'), ]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
@ -313,3 +314,5 @@ CAPTCHA_IMAGE_SIZE = (75, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100'
#
SESSION_COOKIE_AGE = 10800 # 3 hours
SESSION_SAVE_EVERY_REQUEST = True

View File

@ -17,7 +17,6 @@ from django.conf.urls import url, include
from django.conf import settings
from django.conf.urls.static import static
from django.views.generic.base import TemplateView
from django.http import HttpResponseRedirect
urlpatterns = [
@ -25,6 +24,7 @@ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
url(r'^(api/)?users/', include('users.urls')),
url(r'^assets/', include('assets.urls')),
url(r'^perms/', include('perms.urls')),
url(r'^terminal/', include('webterminal.urls')),
]

Binary file not shown.

File diff suppressed because it is too large Load Diff

36
apps/perms/forms.py Normal file
View File

@ -0,0 +1,36 @@
# ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
from django import forms
from django.utils.translation import ugettext_lazy as _
# from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
from .models import AssetPermission
class AssetPermissionForm(forms.ModelForm):
class Meta:
model = AssetPermission
fields = [
'name', 'users', 'user_groups', 'assets', 'asset_groups',
'system_users', 'action', 'is_active', 'date_expired', 'comment',
]
widgets = {
'users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select users')}),
'user_groups': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select user groups')}),
'assets': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select assets')}),
'asset_groups': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')}),
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
}
help_texts = {
'name': '* required',
'user_groups': '* User or user group at least one required',
'asset_groups': '* Asset or Asset group at least one required',
'system_users': '* required',
}

12
apps/perms/hands.py Normal file
View File

@ -0,0 +1,12 @@
# ~*~ coding: utf-8 ~*~
#
from django.db import models
from django.utils.translation import ugettext_lazy as _
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser

View File

@ -1,5 +1,70 @@
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
import functools
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser
from common.utils import date_expired_default, combine_seq
class AssetPermission(models.Model):
ACTION_CHOICE = (
('1', 'Allow'),
('0', 'Deny'),
)
name = models.CharField(max_length=128, verbose_name=_('Name'))
users = models.ManyToManyField(User, related_name='asset_permissions', blank=True)
user_groups = models.ManyToManyField(UserGroup, related_name='asset_permissions', blank=True)
assets = models.ManyToManyField(Asset, related_name='granted_by_permissions', blank=True)
asset_groups = models.ManyToManyField(AssetGroup, related_name='granted_by_permissions', blank=True)
system_users = models.ManyToManyField(SystemUser, related_name='granted_by_permissions')
action = models.CharField(choices=ACTION_CHOICE, max_length=8, default='1')
is_active = models.BooleanField(default=True, verbose_name=_('Active'))
date_expired = models.DateTimeField(default=date_expired_default, verbose_name=_('Date expired'))
created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now=True, verbose_name=_('Date created'))
comment = models.TextField(verbose_name=_('Comment'), blank=True)
def __unicode__(self):
return '%(name)s: %(action)s' % {'name': self.name, 'action': self.action}
@property
def is_valid(self):
if self.date_expired < timezone.now() and self.is_active:
return True
return True
@staticmethod
def set_inherit(obj):
setattr(obj, 'inherited', True)
return obj
def get_granted_users(self):
return list(set(self.users.all() or []) | set(self.get_granted_user_groups_member()))
def get_granted_user_groups_member(self):
combine_users = functools.partial(combine_seq, callback=AssetPermission.set_inherit)
try:
return functools.reduce(combine_users, [user_group.users.all()
for user_group in self.user_groups.iterator()])
except TypeError:
return []
def get_granted_assets(self):
return list(set(self.assets.all() or []) | set(self.get_granted_asset_groups_member()))
def get_granted_asset_groups_member(self):
combine_assets = functools.partial(combine_seq, callback=AssetPermission.set_inherit)
try:
return functools.reduce(combine_assets, [asset_group.users.all()
for asset_group in self.asset_groups.iterator()])
except TypeError:
return []
class Meta:
db_table = 'asset_permission'
# Create your models here.

View File

@ -0,0 +1,196 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'perms:asset-permission-user-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li class="active">
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset gruops' %}</a>
</li>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
<div class="input-group-btn">
<button id="search_btn" type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ asset_permission.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for asset in page_obj %}
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>{{ user.port }}</td>
<td>
{% if asset.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset to this permission' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select asset ' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset in asset_remain %}
<option value="{{ asset.id }}">{{ asset.hostname }}: {{ asset.ip }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add asset group to this permission' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select asset groups' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset_group in asset_groups_remain %}
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for asset_group in asset_groups %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,96 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create asset permission ' %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
<h3>{% trans 'Name' %}</h3>
{{ form.name|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'User' %}</h3>
{{ form.users|bootstrap_horizontal }}
{{ form.user_groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Asset' %}</h3>
{{ form.assets|bootstrap_horizontal }}
{{ form.asset_groups|bootstrap_horizontal }}
{{ form.system_users |bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{{ form.action|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.is_active.id_for_label }}" class="col-sm-2 control-label">{% trans 'Active' %}</label>
<div class="col-sm-8">
{{ form.is_active }}
</div>
</div>
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
})
})
</script>
{% endblock %}

View File

@ -0,0 +1,220 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'perms:asset-permission-user-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li>
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset gruops' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ asset_permission.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ asset_permission.name }}</b></td>
</tr>
<tr>
<td>{% trans 'User count' %}:</td>
<td><b>{{ asset_permission.users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'User group count' %}:</td>
<td><b>{{ asset_permission.users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset count' %}:</td>
<td><b>{{ asset_permission.assets.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset group count' %}:</td>
<td><b>{{ asset_permission.asset_groups.count }}</b></td>
</tr>
<tr>
<td>{% trans 'System user count' %}:</td>
<td><b>{{ asset_permission.system_users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Action' %}:</td>
<td><b>{{ asset_permission.get_action_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Is active' %}:</td>
<td><b>{{ asset_permission.is_active|yesno:'Yes, No, Unkown' }}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ asset_permission.date_expired }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ asset_permission.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ asset_permission.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ asset_permission.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">Active:</td>
<td><span style="float: right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if asset_permission.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
</span>
</td>
</tr>
<tr>
<td width="50%">{% trans 'Repush system user' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Push' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'System user' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users_remain %}
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}: {{ system_user.username }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for system_user in system_users %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ system_user.id }}>{{ system_user.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'perms:asset-permission-create' %}" class="btn btn-sm btn-primary "> {% trans "Create permission" %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th class="text-center"><a href="{% url 'perms:asset-permission-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'User count' %}</th>
<th class="text-center">{% trans 'User group count' %}</th>
<th class="text-center">{% trans 'Asset count' %}</th>
<th class="text-center">{% trans 'Asset group count' %}</th>
<th class="text-center">{% trans 'System user count' %}</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">{% trans 'Is valid' %}</a></th>
<th class="text-center"></th>
{% endblock %}
{% block table_body %}
{% for asset_permission in asset_permission_list %}
<tr class="gradeX">
<td class="text-center">
<input type="checkbox" name="checked" value="{{ asset_permission.id }}">
</td>
<td class="text-center">
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}">
{{ asset_permission.name }}
</a>
</td>
<td class="text-center">{{ asset_permission.users.count}}</td>
<td class="text-center">{{ asset_permission.user_groups.count}}</td>
<td class="text-center">{{ asset_permission.assets.count }}</td>
<td class="text-center">{{ asset_permission.asset_groups.count }}</td>
<td class="text-center">{{ asset_permission.system_users.count }}</td>
<td class="text-center">
{% if asset_permission.is_valid %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a href="{% url 'perms:asset-permission-delete' pk=asset_permission.id %}" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}
{% block content_bottom_left %}
<form id="" method="get" action="" class=" mail-search">
<div class="input-group">
<select class="form-control m-b" style="width: auto">
<option>{% trans 'Delete selected' %}</option>
<option>{% trans 'Update selected' %}</option>
<option>{% trans 'Deactive selected' %}</option>
<option>{% trans 'Export selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,196 @@
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active">
<a href="{% url 'perms:asset-permission-user-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li>
<a href="{% url 'perms:asset-permission-asset-list' pk=asset_permission.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset gruops' %}</a>
</li>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
<div class="input-group-btn">
<button id="search_btn" type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'User list of ' %} <b>{{ asset_permission.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Username' %}</th>
<th>{% trans 'Email' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for user in page_obj %}
<tr>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>
{% if user.is_expired and user.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group {% if user.inherited %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add user to asset permission' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for user in users_remain %}
<option value="{{ user.id }}">{{ user.name }}: {{ user.username }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add user group to asset permission' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select user groups' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for user_group in user_groups_remain %}
<option value="{{ user_group.id }}" id="opt_{{ user_group.id }}">{{ user_group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for user_group in user_groups %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ user_group.id }}>{{ user_group.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
{# function switch_user_status(obj) {#}
{# var status = $(obj).prop('checked');#}
{##}
{# $.ajax({#}
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans 'Confirm delete' %}</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object.name }}"?</p>
<input type="submit" value="Confirm" />
</form>
</body>
</html>

View File

@ -0,0 +1,66 @@
{% extends '_list_base.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'users:user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create perm " %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=username">{% trans 'Username' %}</a></th>
<th class="text-center">{% trans 'Role' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Asset group' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center"><a href="{% url 'users:user-list' %}?sort=date_expired">{% trans 'Active' %}</a></th>
<th class="text-center"></th>
{% endblock %}
{% block table_body %}
{% for perm in page_obj %}
<tr class="gradeX">
<td class="text-center">
<input type="checkbox" name="checked" value="{{ user.id }}">
</td>
<td class="text-center">
<a href="{% url 'users:user-detail' pk=user.id %}">
{{ user.name }}
</a>
</td>
<td class="text-center">{{ user.username }}</td>
<td class="text-center">{{ user.get_role_display }}</td>
<th class="text-center">35/40</th>
<th class="text-center">20</th>
<th class="text-center">3</th>
<td class="text-center">
<a href="{% url 'perms:perm-user-asset-create' pk=user.id %}" class="btn btn-xs btn-info">{% trans 'Create perm' %}</a>
<a href="{% url 'users:user-delete' pk=user.id %}" class="btn btn-xs btn-danger del {% if user.id == request.user.id or user.username == 'admin' %} disabled {% endif %}">{% trans 'Flush' %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}
{% block content_bottom_left %}
<form id="" method="get" action="" class=" mail-search">
<div class="input-group">
<select class="form-control m-b" style="width: auto">
<option>{% trans 'Delete selected' %}</option>
<option>{% trans 'Update selected' %}</option>
<option>{% trans 'Deactive selected' %}</option>
<option>{% trans 'Export selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -1 +1,22 @@
# coding:utf-8
from django.conf.urls import url
import views
app_name = 'perms'
urlpatterns = [
url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
url(r'^asset-permission/(?P<pk>[0-9]+)/update$', views.AssetPermissionUpdateView.as_view(),
name='asset-permission-update'),
url(r'^asset-permission/(?P<pk>[0-9]+)$', views.AssetPermissionDetailView.as_view(),
name='asset-permission-detail'),
url(r'^asset-permission/(?P<pk>[0-9]+)/delete$', views.AssetPermissionDeleteView.as_view(),
name='asset-permission-delete'),
url(r'^asset-permission/(?P<pk>[0-9]+)/user$', views.AssetPermissionUserListView.as_view(),
name='asset-permission-user-list'),
url(r'^asset-permission/(?P<pk>[0-9]+)/asset$', views.AssetPermissionAssetListView.as_view(),
name='asset-permission-asset-list'),
]

View File

@ -1,3 +1,190 @@
from django.shortcuts import render
# ~*~ coding: utf-8 ~*~
# Create your views here.
from __future__ import unicode_literals, absolute_import
import functools
from django.utils.translation import ugettext as _
from django.conf import settings
from django.db.models import Q
from django.views.generic import TemplateView, ListView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from common.utils import search_object_attr
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, Asset, AssetGroup
from .models import AssetPermission
from .forms import AssetPermissionForm
class AssetPermissionListView(AdminUserRequiredMixin, ListView):
model = AssetPermission
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'asset_permission_list'
template_name = 'perms/asset_permission_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Asset permission list'),
'keyword': self.keyword,
}
kwargs.update(context)
return super(AssetPermissionListView, self).get_context_data(**kwargs)
def get_queryset(self):
# Todo: Default order by lose asset connection num
self.queryset = super(AssetPermissionListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(users__name__contains=keyword) |
Q(users__username__contains=keyword) |
Q(user_groups__name__contains=keyword) |
Q(assets__ip__contains=keyword) |
Q(assets__hostname__contains=keyword) |
Q(system_users__username__icontains=keyword) |
Q(system_users__name__icontains=keyword) |
Q(asset_groups__name__icontains=keyword) |
Q(comment__icontains=keyword) |
Q(name__icontains=keyword)).distinct()
if sort:
self.queryset = self.queryset.order_by(sort)
return self.queryset
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy('perms:asset-permission-list')
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Create asset permission'),
}
kwargs.update(context)
return super(AssetPermissionCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _('Create asset permission <a href="%s"> %s </a> successfully.' %
(
reverse_lazy('perms:asset-permission-detail', kwargs={'pk': self.object.pk}),
self.object.name,
))
return success_message
class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_message = _('Update asset permission <a href="%s"> %s </a> successfully.')
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Update asset permission')
}
kwargs.update(context)
return super(AssetPermissionUpdateView, self).get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('perms:asset-permission-detail', kwargs={'pk': self.object.pk})
return success_url
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'perms/asset_permission_detail.html'
context_object_name = 'asset_permission'
model = AssetPermission
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Asset permission detail'),
'system_users_remain': [system_user for system_user in SystemUser.objects.all()
if system_user not in self.object.system_users.all()],
'system_users': self.object.system_users.all(),
}
kwargs.update(context)
return super(AssetPermissionDetailView, self).get_context_data(**kwargs)
class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
model = AssetPermission
template_name = 'perms/delete_confirm.html'
success_url = reverse_lazy('perms:asset-permission-list')
class AssetPermissionUserListView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
template_name = 'perms/asset_permission_user_list.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all())
self.keyword = self.request.GET.get('keyword', '')
return super(AssetPermissionUserListView, self).get(request, *args, **kwargs)
def get_queryset(self):
queryset = self.object.get_granted_users()
if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword,
attr_list=['username', 'name', 'email'],
ignore_case=True)
queryset = filter(search_func, queryset)
return queryset
def get_context_data(self, **kwargs):
users_granted = self.get_queryset()
user_groups_granted = self.object.user_groups.all()
context = {
'app': _('Perms'),
'action': _('Asset permission user list'),
'users_remain': [user for user in User.objects.all() if user not in users_granted],
'user_groups': self.object.user_groups.all(),
'user_groups_remain': [user_group for user_group in UserGroup.objects.all()
if user_group not in user_groups_granted],
'keyword': self.keyword,
}
kwargs.update(context)
return super(AssetPermissionUserListView, self).get_context_data(**kwargs)
class AssetPermissionAssetListView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
template_name = 'perms/asset_permission_asset_list.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all())
self.keyword = self.request.GET.get('keyword', '')
return super(AssetPermissionAssetListView, self).get(request, *args, **kwargs)
def get_queryset(self):
queryset = self.object.get_granted_assets()
if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword,
attr_list=['hostname', 'ip'],
ignore_case=True)
queryset = filter(search_func, queryset)
return queryset
def get_context_data(self, **kwargs):
assets_granted = self.get_queryset()
asset_groups_granted = self.object.user_groups.all()
context = {
'app': _('Perms'),
'action': _('Asset permission asset list'),
'assets_remain': (asset for asset in Asset.objects.all() if asset not in assets_granted),
'asset_groups': self.object.asset_groups.all(),
'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
if asset_group not in asset_groups_granted],
'keyword': self.keyword,
}
kwargs.update(context)
return super(AssetPermissionAssetListView, self).get_context_data(**kwargs)

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -38,6 +38,7 @@ th a {
color: white;
}
.select2-selection--single,
.select2-selection--multiple {
border: 1px solid #e5e6e7 !important;
cursor: text !important;

View File

@ -0,0 +1,715 @@
body.stop-scrolling {
height: 100%;
overflow: hidden; }
.sweet-overlay {
background-color: black;
/* IE8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
/* IE8 */
background-color: rgba(0, 0, 0, 0.4);
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: none;
z-index: 10000; }
.sweet-alert {
background-color: white;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
width: 478px;
padding: 17px;
border-radius: 5px;
text-align: center;
position: fixed;
left: 50%;
top: 50%;
margin-left: -256px;
margin-top: -200px;
overflow: hidden;
display: none;
z-index: 99999; }
@media all and (max-width: 540px) {
.sweet-alert {
width: auto;
margin-left: 0;
margin-right: 0;
left: 15px;
right: 15px; } }
.sweet-alert h2 {
color: #575757;
font-size: 30px;
text-align: center;
font-weight: 600;
text-transform: none;
position: relative;
margin: 25px 0;
padding: 0;
line-height: 40px;
display: block; }
.sweet-alert p {
color: #797979;
font-size: 16px;
text-align: center;
font-weight: 300;
position: relative;
text-align: inherit;
float: none;
margin: 0;
padding: 0;
line-height: normal; }
.sweet-alert fieldset {
border: none;
position: relative; }
.sweet-alert .sa-error-container {
background-color: #f1f1f1;
margin-left: -17px;
margin-right: -17px;
overflow: hidden;
padding: 0 10px;
max-height: 0;
webkit-transition: padding 0.15s, max-height 0.15s;
transition: padding 0.15s, max-height 0.15s; }
.sweet-alert .sa-error-container.show {
padding: 10px 0;
max-height: 100px;
webkit-transition: padding 0.2s, max-height 0.2s;
transition: padding 0.25s, max-height 0.25s; }
.sweet-alert .sa-error-container .icon {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #ea7d7d;
color: white;
line-height: 24px;
text-align: center;
margin-right: 3px; }
.sweet-alert .sa-error-container p {
display: inline-block; }
.sweet-alert .sa-input-error {
position: absolute;
top: 29px;
right: 26px;
width: 20px;
height: 20px;
opacity: 0;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
-webkit-transition: all 0.1s;
transition: all 0.1s; }
.sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after {
content: "";
width: 20px;
height: 6px;
background-color: #f06e57;
border-radius: 3px;
position: absolute;
top: 50%;
margin-top: -4px;
left: 50%;
margin-left: -9px; }
.sweet-alert .sa-input-error::before {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg); }
.sweet-alert .sa-input-error::after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
.sweet-alert .sa-input-error.show {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(1); }
.sweet-alert input {
width: 100%;
box-sizing: border-box;
border-radius: 3px;
border: 1px solid #d7d7d7;
height: 43px;
margin-top: 10px;
margin-bottom: 17px;
font-size: 18px;
box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06);
padding: 0 12px;
display: none;
-webkit-transition: all 0.3s;
transition: all 0.3s; }
.sweet-alert input:focus {
outline: none;
box-shadow: 0px 0px 3px #c4e6f5;
border: 1px solid #b4dbed; }
.sweet-alert input:focus::-moz-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input:focus:-ms-input-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input:focus::-webkit-input-placeholder {
transition: opacity 0.3s 0.03s ease;
opacity: 0.5; }
.sweet-alert input::-moz-placeholder {
color: #bdbdbd; }
.sweet-alert input:-ms-input-placeholder {
color: #bdbdbd; }
.sweet-alert input::-webkit-input-placeholder {
color: #bdbdbd; }
.sweet-alert.show-input input {
display: block; }
.sweet-alert button {
background-color: #AEDEF4;
color: white;
border: none;
box-shadow: none;
font-size: 17px;
font-weight: 500;
-webkit-border-radius: 4px;
border-radius: 5px;
padding: 10px 32px;
margin: 26px 5px 0 5px;
cursor: pointer; }
.sweet-alert button:focus {
outline: none;
box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05); }
.sweet-alert button:hover {
background-color: #a1d9f2; }
.sweet-alert button:active {
background-color: #81ccee; }
.sweet-alert button.cancel {
background-color: #D0D0D0; }
.sweet-alert button.cancel:hover {
background-color: #c8c8c8; }
.sweet-alert button.cancel:active {
background-color: #b6b6b6; }
.sweet-alert button.cancel:focus {
box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; }
.sweet-alert button::-moz-focus-inner {
border: 0; }
.sweet-alert[data-has-cancel-button=false] button {
box-shadow: none !important; }
.sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] {
padding-bottom: 40px; }
.sweet-alert .sa-icon {
width: 80px;
height: 80px;
border: 4px solid gray;
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%;
margin: 20px auto;
padding: 0;
position: relative;
box-sizing: content-box; }
.sweet-alert .sa-icon.sa-error {
border-color: #F27474; }
.sweet-alert .sa-icon.sa-error .sa-x-mark {
position: relative;
display: block; }
.sweet-alert .sa-icon.sa-error .sa-line {
position: absolute;
height: 5px;
width: 47px;
background-color: #F27474;
display: block;
top: 37px;
border-radius: 2px; }
.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
left: 17px; }
.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
right: 16px; }
.sweet-alert .sa-icon.sa-warning {
border-color: #F8BB86; }
.sweet-alert .sa-icon.sa-warning .sa-body {
position: absolute;
width: 5px;
height: 47px;
left: 50%;
top: 10px;
-webkit-border-radius: 2px;
border-radius: 2px;
margin-left: -2px;
background-color: #F8BB86; }
.sweet-alert .sa-icon.sa-warning .sa-dot {
position: absolute;
width: 7px;
height: 7px;
-webkit-border-radius: 50%;
border-radius: 50%;
margin-left: -3px;
left: 50%;
bottom: 10px;
background-color: #F8BB86; }
.sweet-alert .sa-icon.sa-info {
border-color: #C9DAE1; }
.sweet-alert .sa-icon.sa-info::before {
content: "";
position: absolute;
width: 5px;
height: 29px;
left: 50%;
bottom: 17px;
border-radius: 2px;
margin-left: -2px;
background-color: #C9DAE1; }
.sweet-alert .sa-icon.sa-info::after {
content: "";
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
margin-left: -3px;
top: 19px;
background-color: #C9DAE1; }
.sweet-alert .sa-icon.sa-success {
border-color: #A5DC86; }
.sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after {
content: '';
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%;
position: absolute;
width: 60px;
height: 120px;
background: white;
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
.sweet-alert .sa-icon.sa-success::before {
-webkit-border-radius: 120px 0 0 120px;
border-radius: 120px 0 0 120px;
top: -7px;
left: -33px;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 60px 60px;
transform-origin: 60px 60px; }
.sweet-alert .sa-icon.sa-success::after {
-webkit-border-radius: 0 120px 120px 0;
border-radius: 0 120px 120px 0;
top: -11px;
left: 30px;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 0px 60px;
transform-origin: 0px 60px; }
.sweet-alert .sa-icon.sa-success .sa-placeholder {
width: 80px;
height: 80px;
border: 4px solid rgba(165, 220, 134, 0.2);
-webkit-border-radius: 40px;
border-radius: 40px;
border-radius: 50%;
box-sizing: content-box;
position: absolute;
left: -4px;
top: -4px;
z-index: 2; }
.sweet-alert .sa-icon.sa-success .sa-fix {
width: 5px;
height: 90px;
background-color: white;
position: absolute;
left: 28px;
top: 8px;
z-index: 1;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg); }
.sweet-alert .sa-icon.sa-success .sa-line {
height: 5px;
background-color: #A5DC86;
display: block;
border-radius: 2px;
position: absolute;
z-index: 2; }
.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
width: 25px;
left: 14px;
top: 46px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg); }
.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
width: 47px;
right: 8px;
top: 38px;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg); }
.sweet-alert .sa-icon.sa-custom {
background-size: contain;
border-radius: 0;
border: none;
background-position: center center;
background-repeat: no-repeat; }
/*
* Animations
*/
@-webkit-keyframes showSweetAlert {
0% {
transform: scale(0.7);
-webkit-transform: scale(0.7); }
45% {
transform: scale(1.05);
-webkit-transform: scale(1.05); }
80% {
transform: scale(0.95);
-webkit-transform: scale(0.95); }
100% {
transform: scale(1);
-webkit-transform: scale(1); } }
@keyframes showSweetAlert {
0% {
transform: scale(0.7);
-webkit-transform: scale(0.7); }
45% {
transform: scale(1.05);
-webkit-transform: scale(1.05); }
80% {
transform: scale(0.95);
-webkit-transform: scale(0.95); }
100% {
transform: scale(1);
-webkit-transform: scale(1); } }
@-webkit-keyframes hideSweetAlert {
0% {
transform: scale(1);
-webkit-transform: scale(1); }
100% {
transform: scale(0.5);
-webkit-transform: scale(0.5); } }
@keyframes hideSweetAlert {
0% {
transform: scale(1);
-webkit-transform: scale(1); }
100% {
transform: scale(0.5);
-webkit-transform: scale(0.5); } }
@-webkit-keyframes slideFromTop {
0% {
top: 0%; }
100% {
top: 50%; } }
@keyframes slideFromTop {
0% {
top: 0%; }
100% {
top: 50%; } }
@-webkit-keyframes slideToTop {
0% {
top: 50%; }
100% {
top: 0%; } }
@keyframes slideToTop {
0% {
top: 50%; }
100% {
top: 0%; } }
@-webkit-keyframes slideFromBottom {
0% {
top: 70%; }
100% {
top: 50%; } }
@keyframes slideFromBottom {
0% {
top: 70%; }
100% {
top: 50%; } }
@-webkit-keyframes slideToBottom {
0% {
top: 50%; }
100% {
top: 70%; } }
@keyframes slideToBottom {
0% {
top: 50%; }
100% {
top: 70%; } }
.showSweetAlert[data-animation=pop] {
-webkit-animation: showSweetAlert 0.3s;
animation: showSweetAlert 0.3s; }
.showSweetAlert[data-animation=none] {
-webkit-animation: none;
animation: none; }
.showSweetAlert[data-animation=slide-from-top] {
-webkit-animation: slideFromTop 0.3s;
animation: slideFromTop 0.3s; }
.showSweetAlert[data-animation=slide-from-bottom] {
-webkit-animation: slideFromBottom 0.3s;
animation: slideFromBottom 0.3s; }
.hideSweetAlert[data-animation=pop] {
-webkit-animation: hideSweetAlert 0.2s;
animation: hideSweetAlert 0.2s; }
.hideSweetAlert[data-animation=none] {
-webkit-animation: none;
animation: none; }
.hideSweetAlert[data-animation=slide-from-top] {
-webkit-animation: slideToTop 0.4s;
animation: slideToTop 0.4s; }
.hideSweetAlert[data-animation=slide-from-bottom] {
-webkit-animation: slideToBottom 0.3s;
animation: slideToBottom 0.3s; }
@-webkit-keyframes animateSuccessTip {
0% {
width: 0;
left: 1px;
top: 19px; }
54% {
width: 0;
left: 1px;
top: 19px; }
70% {
width: 50px;
left: -8px;
top: 37px; }
84% {
width: 17px;
left: 21px;
top: 48px; }
100% {
width: 25px;
left: 14px;
top: 45px; } }
@keyframes animateSuccessTip {
0% {
width: 0;
left: 1px;
top: 19px; }
54% {
width: 0;
left: 1px;
top: 19px; }
70% {
width: 50px;
left: -8px;
top: 37px; }
84% {
width: 17px;
left: 21px;
top: 48px; }
100% {
width: 25px;
left: 14px;
top: 45px; } }
@-webkit-keyframes animateSuccessLong {
0% {
width: 0;
right: 46px;
top: 54px; }
65% {
width: 0;
right: 46px;
top: 54px; }
84% {
width: 55px;
right: 0px;
top: 35px; }
100% {
width: 47px;
right: 8px;
top: 38px; } }
@keyframes animateSuccessLong {
0% {
width: 0;
right: 46px;
top: 54px; }
65% {
width: 0;
right: 46px;
top: 54px; }
84% {
width: 55px;
right: 0px;
top: 35px; }
100% {
width: 47px;
right: 8px;
top: 38px; } }
@-webkit-keyframes rotatePlaceholder {
0% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
5% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
12% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); }
100% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } }
@keyframes rotatePlaceholder {
0% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
5% {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg); }
12% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); }
100% {
transform: rotate(-405deg);
-webkit-transform: rotate(-405deg); } }
.animateSuccessTip {
-webkit-animation: animateSuccessTip 0.75s;
animation: animateSuccessTip 0.75s; }
.animateSuccessLong {
-webkit-animation: animateSuccessLong 0.75s;
animation: animateSuccessLong 0.75s; }
.sa-icon.sa-success.animate::after {
-webkit-animation: rotatePlaceholder 4.25s ease-in;
animation: rotatePlaceholder 4.25s ease-in; }
@-webkit-keyframes animateErrorIcon {
0% {
transform: rotateX(100deg);
-webkit-transform: rotateX(100deg);
opacity: 0; }
100% {
transform: rotateX(0deg);
-webkit-transform: rotateX(0deg);
opacity: 1; } }
@keyframes animateErrorIcon {
0% {
transform: rotateX(100deg);
-webkit-transform: rotateX(100deg);
opacity: 0; }
100% {
transform: rotateX(0deg);
-webkit-transform: rotateX(0deg);
opacity: 1; } }
.animateErrorIcon {
-webkit-animation: animateErrorIcon 0.5s;
animation: animateErrorIcon 0.5s; }
@-webkit-keyframes animateXMark {
0% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
50% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
80% {
transform: scale(1.15);
-webkit-transform: scale(1.15);
margin-top: -6px; }
100% {
transform: scale(1);
-webkit-transform: scale(1);
margin-top: 0;
opacity: 1; } }
@keyframes animateXMark {
0% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
50% {
transform: scale(0.4);
-webkit-transform: scale(0.4);
margin-top: 26px;
opacity: 0; }
80% {
transform: scale(1.15);
-webkit-transform: scale(1.15);
margin-top: -6px; }
100% {
transform: scale(1);
-webkit-transform: scale(1);
margin-top: 0;
opacity: 1; } }
.animateXMark {
-webkit-animation: animateXMark 0.5s;
animation: animateXMark 0.5s; }
@-webkit-keyframes pulseWarning {
0% {
border-color: #F8D486; }
100% {
border-color: #F8BB86; } }
@keyframes pulseWarning {
0% {
border-color: #F8D486; }
100% {
border-color: #F8BB86; } }
.pulseWarning {
-webkit-animation: pulseWarning 0.75s infinite alternate;
animation: pulseWarning 0.75s infinite alternate; }
@-webkit-keyframes pulseWarningIns {
0% {
background-color: #F8D486; }
100% {
background-color: #F8BB86; } }
@keyframes pulseWarningIns {
0% {
background-color: #F8D486; }
100% {
background-color: #F8BB86; } }
.pulseWarningIns {
-webkit-animation: pulseWarningIns 0.75s infinite alternate;
animation: pulseWarningIns 0.75s infinite alternate; }
/* Internet Explorer 9 has some special quirks that are fixed here */
/* The icons are not animated. */
/* This file is automatically merged into sweet-alert.min.js through Gulp */
/* Error icon */
.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
-ms-transform: rotate(45deg) \9; }
.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
-ms-transform: rotate(-45deg) \9; }
/* Success icon */
.sweet-alert .sa-icon.sa-success {
border-color: transparent\9; }
.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
-ms-transform: rotate(45deg) \9; }
.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
-ms-transform: rotate(-45deg) \9; }

View File

@ -0,0 +1,222 @@
.toast-title {
font-weight: 700
}
.toast-message {
-ms-word-wrap: break-word;
word-wrap: break-word
}
.toast-message a, .toast-message label {
color: #fff
}
.toast-message a:hover {
color: #ccc;
text-decoration: none
}
.toast-close-button {
position: relative;
right: -.3em;
top: -.3em;
float: right;
font-size: 20px;
font-weight: 700;
color: #fff;
-webkit-text-shadow: 0 1px 0 #fff;
text-shadow: 0 1px 0 #fff;
opacity: .8;
-ms-filter: alpha(Opacity=80);
filter: alpha(opacity=80)
}
.toast-close-button:focus, .toast-close-button:hover {
color: #000;
text-decoration: none;
cursor: pointer;
opacity: .4;
-ms-filter: alpha(Opacity=40);
filter: alpha(opacity=40)
}
button.toast-close-button {
padding: 0;
cursor: pointer;
background: 0 0;
border: 0;
-webkit-appearance: none
}
.toast-top-center {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%
}
.toast-top-left {
top: 12px;
left: 12px
}
.toast-top-right {
top: 12px;
right: 12px
}
.toast-bottom-right {
right: 12px;
bottom: 12px
}
.toast-bottom-left {
bottom: 12px;
left: 12px
}
#toast-container {
position: fixed;
z-index: 999999
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
#toast-container > div {
position: relative;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999;
-webkit-box-shadow: 0 0 12px #999;
box-shadow: 0 0 12px #999;
color: #fff;
opacity: .8;
-ms-filter: alpha(Opacity=80);
filter: alpha(opacity=80)
}
#toast-container > :hover {
-moz-box-shadow: 0 0 12px #000;
-webkit-box-shadow: 0 0 12px #000;
box-shadow: 0 0 12px #000;
opacity: 1;
-ms-filter: alpha(Opacity=100);
filter: alpha(opacity=100);
cursor: pointer
}
#toast-container > .toast-info {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=) !important
}
#toast-container > .toast-success {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==) !important
}
#toast-container > .toast-warning {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=) !important
}
#toast-container.toast-bottom-center > div, #toast-container.toast-top-center > div {
width: 300px;
margin: auto
}
#toast-container.toast-bottom-full-width > div, #toast-container.toast-top-full-width > div {
width: 96%;
margin: auto
}
.toast {
background-color: #030303
}
.toast-success {
background-color: #51a351
}
.toast-error {
background-color: #bd362f
}
.toast-info {
background-color: #2f96b4
}
.toast-warning {
background-color: #f89406
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000;
opacity: .4;
-ms-filter: alpha(Opacity=40);
filter: alpha(opacity=40)
}
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 241px) and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em
}
#toast-container .toast-close-button {
right: -.2em;
top: -.2em
}
}
@media all and (min-width: 481px) and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em
}
}

View File

@ -0,0 +1,3 @@
[.ShellClassInfo]
IconFile=%SystemRoot%\system32\SHELL32.dll
IconIndex=161

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 B

View File

@ -0,0 +1,43 @@
@charset "utf-8";
/*
CSS border:dotted solid double dashed
*/
*:focus {outline: none;}
/* fade */
.jbox-fade{background-color:#000000;}
/* drag */
.jbox-drag{border:1px dashed #003870;}
/* jbox */
div.jbox {padding:0px;border:none;font-size:12px;}
/* border */
div.jbox .jbox-border{background: none repeat scroll 0 0 #000000;filter:alpha(opacity=20);-moz-opacity:0.2;opacity:0.2;}
/* container */
div.jbox .jbox-container{background-color:#ffffff;border:1px solid #999999;}
/* title-panel */
div.jbox .jbox-title-panel{background:#2f4050;border-bottom:1px solid #CCCCCC;}
div.jbox .jbox-title{font-weight:bold;color:#ffffff;}
div.jbox .jbox-title-icon{background:url(images/jbox-title-icon.gif) no-repeat scroll 3px 5px transparent;}
div.jbox .jbox-close,div.jbox .jbox-close-hover{background:url(images/jbox-close1.gif) no-repeat scroll 0px 0px transparent;}
div.jbox .jbox-close-hover{background-position:-16px 0;}
/* content */
div.jbox .jbox-content{min-height:24px;line-height:18px;color:#444444;}
div.jbox .jbox-content-loading{background-color:#E6E6E6;}
div.jbox .jbox-content-loading-image{background:url(images/jbox-content-loading.gif) no-repeat bottom center;}
/* button-panel */
div.jbox .jbox-button-panel{border-top:1px solid #CCCCCC;background-color: #EEEEEE;}
div.jbox .jbox-bottom-text{text-indent:10px;color:#444444;}
div.jbox .jbox-button{background:url(images/jbox-button2.png) repeat-x transparent;border:#AAAAAA 1px solid;color:#888888;border-radius:3px 3px 3px 3px;margin:1px 7px 0px 0px;height:22px;cursor:default;}
div.jbox .jbox-button-hover{background-position:0px -20px;color:#666666;}
div.jbox .jbox-button-active{background-position:0px -40px;}
div.jbox-warning .jbox .jbox-button-panel{background-color: #FFFFFF;}
/* tip-color */
div.jbox .jbox-tip-color{background-color:#003870;border-color:#003870;border-radius:3px 3px 3px 3px;color:#ffffff;}
/* icons */
div.jbox span.jbox-icon{background:url(images/jbox-icons.png) no-repeat scroll 0 0 transparent;_background:url(images/jbox-icons-ie6.gif) no-repeat scroll 0 0 transparent;}
div.jbox span.jbox-icon-info {background-position:0 0;}
div.jbox span.jbox-icon-question {background-position:-36px 0;}
div.jbox span.jbox-icon-success {background-position:-72px 0;}
div.jbox span.jbox-icon-warning {background-position:-108px 0;}
div.jbox span.jbox-icon-error {background-position:-144px 0;}
div.jbox span.jbox-icon-none {display: none; overflow:hidden;}
div.jbox span.jbox-icon-loading {background:url(images/jbox-loading1.gif) no-repeat scroll 0 0 transparent;}

View File

@ -0,0 +1,3 @@
[.ShellClassInfo]
IconFile=%SystemRoot%\system32\SHELL32.dll
IconIndex=161

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 B

View File

@ -0,0 +1,41 @@
@charset "utf-8";
*:focus {outline: none;}
/* fade */
.jbox-fade{background-color:#000000;}
/* drag */
.jbox-drag{border:1px dashed #0097d4;}
/* jbox */
div.jbox {padding:0px;border:none;font-size:12px;}
/* border */
div.jbox .jbox-border{background: none repeat scroll 0 0 #000000;filter:alpha(opacity=20);-moz-opacity:0.2;opacity:0.2;}
/* container */
div.jbox .jbox-container{background-color:#ffffff;border:1px solid #999999;}
/* title-panel */
div.jbox .jbox-title-panel{background: #2f4050;background: -webkit-gradient(linear, left top, left bottom, from(#2f4050), to(#2f4050));background: -moz-linear-gradient(top, #2f4050, #2f4050);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2f4050', endColorstr='#2f4050');border-bottom:1px solid #999999;}
div.jbox .jbox-title{font-weight:bold;color:#ffffff;}
div.jbox .jbox-title-icon{background:url(images/jbox-title-icon.gif) no-repeat scroll 3px 5px transparent;}
div.jbox .jbox-close,div.jbox .jbox-close-hover{background:url(images/jbox-close1.gif) no-repeat scroll 0px 0px transparent;}
div.jbox .jbox-close-hover{background-position:-16px 0;}
/* content */
div.jbox .jbox-content{min-height:24px;line-height:18px;color:#444444;}
div.jbox .jbox-content-loading{background-color:#E6E6E6;}
div.jbox .jbox-content-loading-image{background:url(images/jbox-content-loading.gif) no-repeat bottom center;}
/* button-panel */
div.jbox .jbox-button-panel{border-top:1px solid #CCCCCC;background-color: #EEEEEE;}
div.jbox .jbox-bottom-text{text-indent:10px;color:#444444;}
div.jbox .jbox-button{background: #0097d4;background: -webkit-gradient(linear, left top, left bottom, from(#2f4050), to(#2f4050));background: -moz-linear-gradient(top, #2f4050, #2f4050);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2f4050', endColorstr='#2f4050');border:#004b6a 1px solid;color:#fff;border-radius:3px 3px 3px 3px;margin:1px 7px 0px 0px;height:22px;cursor:default;}
div.jbox .jbox-button-hover{background: #0097d4;background: -webkit-gradient(linear, left top, left bottom, from(#0097d4), to(#005b7f));background: -moz-linear-gradient(top, #0097d4, #005b7f);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0097d4', endColorstr='#005b7f');}
div.jbox .jbox-button-active{background: -webkit-gradient(linear, left top, left bottom, from(#005b7f), to(#0097d4));background: -moz-linear-gradient(top, #005b7f, #0097d4);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#005b7f', endColorstr='#0097d4');}
div.jbox-warning .jbox .jbox-button-panel{background-color: #FFFFFF;}
/* tip-color */
div.jbox .jbox-tip-color{background: #0097d4;background: -webkit-gradient(linear, left top, left bottom, from(#0097d4), to(#005b7f));background: -moz-linear-gradient(top, #0097d4, #005b7f);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0097d4', endColorstr='#005b7f');border-color:#004b6a;border-radius:3px 3px 3px 3px;color:#fff;}
/* icons */
div.jbox span.jbox-icon{background:url(images/jbox-icons.png) no-repeat scroll 0 0 transparent;_background:url(images/jbox-icons-ie6.gif) no-repeat scroll 0 0 transparent;}
div.jbox span.jbox-icon-info {background-position:0 0;}
div.jbox span.jbox-icon-question {background-position:-36px 0;}
div.jbox span.jbox-icon-success {background-position:-72px 0;}
div.jbox span.jbox-icon-warning {background-position:-108px 0;}
div.jbox span.jbox-icon-error {background-position:-144px 0;}
div.jbox span.jbox-icon-none {display: none; overflow:hidden;}
div.jbox span.jbox-icon-loading {background:url(images/jbox-loading1.gif) no-repeat scroll 0 0 transparent;}

View File

@ -0,0 +1,77 @@

/* jBox 全局设置 */
var jBoxConfig = {};
jBoxConfig.defaults = {
id: null, /* 在页面中的唯一id如果为null则自动生成随机id,一个id只会显示一个jBox */
top: '15%', /* 窗口离顶部的距离,可以是百分比或像素(如 '100px') */
border: 5, /* 窗口的外边框像素大小,必须是0以上的整数 */
opacity: 0.1, /* 窗口隔离层的透明度,如果设置为0,则不显示隔离层 */
timeout: 0, /* 窗口显示多少毫秒后自动关闭,如果设置为0,则不自动关闭 */
showType: 'fade', /* 窗口显示的类型,可选值有:show、fade、slide */
showSpeed: 'fast', /* 窗口显示的速度,可选值有:'slow'、'fast'、表示毫秒的整数 */
showIcon: true, /* 是否显示窗口标题的图标true显示false不显示或自定义的CSS样式类名以为图标为背景 */
showClose: true, /* 是否显示窗口右上角的关闭按钮 */
draggable: true, /* 是否可以拖动窗口 */
dragLimit: true, /* 在可以拖动窗口的情况下,是否限制在可视范围 */
dragClone: false, /* 在可以拖动窗口的情况下,鼠标按下时窗口是否克隆窗口 */
persistent: true, /* 在显示隔离层的情况下,点击隔离层时,是否坚持窗口不关闭 */
showScrolling: true, /* 是否显示浏览的滚动条 */
ajaxData: {}, /* 在窗口内容使用get:或post:前缀标识的情况下ajax post的数据例如{ id: 1 } 或 "id=1" */
iframeScrolling: 'auto', /* 在窗口内容使用iframe:前缀标识的情况下iframe的scrolling属性值可选值有'auto'、'yes'、'no' */
title: 'jBox', /* 窗口的标题 */
width: 350, /* 窗口的宽度,值为'auto'或表示像素的整数 */
height: 'auto', /* 窗口的高度,值为'auto'或表示像素的整数 */
bottomText: '', /* 窗口的按钮左边的内容,当没有按钮时此设置无效 */
buttons: { '确定': 'ok' }, /* 窗口的按钮 */
buttonsFocus: 0, /* 表示第几个按钮为默认按钮索引从0开始 */
loaded: function (h) { }, /* 窗口加载完成后执行的函数需要注意的是如果是ajax或iframe也是要等加载完http请求才算窗口加载完成参数h表示窗口内容的jQuery对象 */
submit: function (v, h, f) { return true; }, /* 点击窗口按钮后的回调函数返回true时表示关闭窗口参数有三个v表示所点的按钮的返回值h表示窗口内容的jQuery对象f表示窗口内容里的form表单键值 */
closed: function () { } /* 窗口关闭后执行的函数 */
};
jBoxConfig.stateDefaults = {
content: '', /* 状态的内容,不支持前缀标识 */
buttons: { '确定': 'ok' }, /* 状态的按钮 */
buttonsFocus: 0, /* 表示第几个按钮为默认按钮索引从0开始 */
submit: function (v, h, f) { return true; } /* 点击状态按钮后的回调函数返回true时表示关闭窗口参数有三个v表示所点的按钮的返回值h表示窗口内容的jQuery对象f表示窗口内容里的form表单键值 */
};
jBoxConfig.tipDefaults = {
content: '', /* 提示的内容,不支持前缀标识 */
icon: 'info', /* 提示的图标,可选值有'info'、'success'、'warning'、'error'、'loading',默认值为'info',当为'loading'时timeout值会被设置为0表示不会自动关闭。 */
top: '40%', /* 提示离顶部的距离,可以是百分比或像素(如 '100px') */
width: 'auto', /* 提示的高度,值为'auto'或表示像素的整数 */
height: 'auto', /* 提示的高度,值为'auto'或表示像素的整数 */
opacity: 0, /* 窗口隔离层的透明度,如果设置为0,则不显示隔离层 */
timeout: 3000, /* 提示显示多少毫秒后自动关闭,必须是大于0的整数 */
closed: function () { } /* 提示关闭后执行的函数 */
};
jBoxConfig.messagerDefaults = {
content: '', /* 信息的内容,不支持前缀标识 */
title: 'jBox', /* 信息的标题 */
icon: 'none', /* 信息图标,值为'none'时为不显示图标,可选值有'none'、'info'、'question'、'success'、'warning'、'error' */
width: 350, /* 信息的高度,值为'auto'或表示像素的整数 */
height: 'auto', /* 信息的高度,值为'auto'或表示像素的整数 */
timeout: 3000, /* 信息显示多少毫秒后自动关闭,如果设置为0,则不自动关闭 */
showType: 'slide', /* 信息显示的类型,可选值有:show、fade、slide */
showSpeed: 600, /* 信息显示的速度,可选值有:'slow'、'fast'、表示毫秒的整数 */
border: 0, /* 信息的外边框像素大小,必须是0以上的整数 */
buttons: {}, /* 信息的按钮 */
buttonsFocus: 0, /* 表示第几个按钮为默认按钮索引从0开始 */
loaded: function (h) { }, /* 窗口加载完成后执行的函数参数h表示窗口内容的jQuery对象 */
submit: function (v, h, f) { return true; }, /* 点击信息按钮后的回调函数返回true时表示关闭窗口参数有三个v表示所点的按钮的返回值h表示窗口内容的jQuery对象f表示窗口内容里的form表单键值 */
closed: function () { } /* 信息关闭后执行的函数 */
};
jBoxConfig.languageDefaults = {
close: '关闭', /* 窗口右上角关闭按钮提示 */
ok: '确定', /* $.jBox.prompt() 系列方法的“确定”按钮文字 */
yes: '是', /* $.jBox.warning() 方法的“是”按钮文字 */
no: '否', /* $.jBox.warning() 方法的“否”按钮文字 */
cancel: '取消' /* $.jBox.confirm() 和 $.jBox.warning() 方法的“取消”按钮文字 */
};
$.jBox.setDefaults(jBoxConfig);

View File

@ -0,0 +1,9 @@
/*!
* jQuery Migrate - v1.1.1 - 2013-02-16
* https://github.com/jquery/jquery-migrate
* Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
*/
(function(s,p,i){var D={};s.migrateWarnings=[];if(!s.migrateMute&&p.console&&console.log){console.log("JQMIGRATE: Logging is active")}if(s.migrateTrace===i){s.migrateTrace=true}s.migrateReset=function(){D={};s.migrateWarnings.length=0};function h(G){if(!D[G]){D[G]=true;s.migrateWarnings.push(G);if(p.console&&console.warn&&!s.migrateMute){console.warn("JQMIGRATE: "+G);if(s.migrateTrace&&console.trace){console.trace()}}}}function a(I,K,H,J){if(Object.defineProperty){try{Object.defineProperty(I,K,{configurable:true,enumerable:true,get:function(){h(J);return H},set:function(L){h(J);H=L}});return}catch(G){}}s._definePropertyBroken=true;I[K]=H}if(document.compatMode==="BackCompat"){h("jQuery is not compatible with Quirks Mode")}var f=s("<input/>",{size:1}).attr("size")&&s.attrFn,x=s.attr,w=s.attrHooks.value&&s.attrHooks.value.get||function(){return null},j=s.attrHooks.value&&s.attrHooks.value.set||function(){return i},t=/^(?:input|button)$/i,y=/^[238]$/,B=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,k=/^(?:checked|selected)$/i;a(s,"attrFn",f||{},"jQuery.attrFn is deprecated");s.attr=function(K,I,L,J){var H=I.toLowerCase(),G=K&&K.nodeType;if(J){if(x.length<4){h("jQuery.fn.attr( props, pass ) is deprecated")}if(K&&!y.test(G)&&(f?I in f:s.isFunction(s.fn[I]))){return s(K)[I](L)}}if(I==="type"&&L!==i&&t.test(K.nodeName)&&K.parentNode){h("Can't change the 'type' of an input or button in IE 6/7/8")}if(!s.attrHooks[H]&&B.test(H)){s.attrHooks[H]={get:function(N,M){var P,O=s.prop(N,M);return O===true||typeof O!=="boolean"&&(P=N.getAttributeNode(M))&&P.nodeValue!==false?M.toLowerCase():i},set:function(N,P,M){var O;if(P===false){s.removeAttr(N,M)}else{O=s.propFix[M]||M;if(O in N){N[O]=true}N.setAttribute(M,M.toLowerCase())}return M}};if(k.test(H)){h("jQuery.fn.attr('"+H+"') may use property instead of attribute")
}}return x.call(s,K,I,L)};s.attrHooks.value={get:function(H,G){var I=(H.nodeName||"").toLowerCase();if(I==="button"){return w.apply(this,arguments)}if(I!=="input"&&I!=="option"){h("jQuery.fn.attr('value') no longer gets properties")}return G in H?H.value:null},set:function(G,H){var I=(G.nodeName||"").toLowerCase();if(I==="button"){return j.apply(this,arguments)}if(I!=="input"&&I!=="option"){h("jQuery.fn.attr('value', val) no longer sets properties")}G.value=H}};var q,E,z=s.fn.init,A=s.parseJSON,v=/^(?:[^<]*(<[\w\W]+>)[^>]*|#([\w\-]*))$/;s.fn.init=function(G,J,I){var H;if(G&&typeof G==="string"&&!s.isPlainObject(J)&&(H=v.exec(G))&&H[1]){if(G.charAt(0)!=="<"){h("$(html) HTML strings must start with '<' character")}if(J&&J.context){J=J.context}if(s.parseHTML){return z.call(this,s.parseHTML(s.trim(G),J,true),J,I)}}return z.apply(this,arguments)};s.fn.init.prototype=s.fn;s.parseJSON=function(G){if(!G&&G!==null){h("jQuery.parseJSON requires a valid JSON string");return null}return A.apply(this,arguments)};s.uaMatch=function(H){H=H.toLowerCase();var G=/(chrome)[ \/]([\w.]+)/.exec(H)||/(webkit)[ \/]([\w.]+)/.exec(H)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(H)||/(msie) ([\w.]+)/.exec(H)||H.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(H)||[];return{browser:G[1]||"",version:G[2]||"0"}};if(!s.browser){q=s.uaMatch(navigator.userAgent);E={};if(q.browser){E[q.browser]=true;E.version=q.version}if(E.chrome){E.webkit=true}else{if(E.webkit){E.safari=true}}s.browser=E}a(s,"browser",s.browser,"jQuery.browser is deprecated");s.sub=function(){function G(J,K){return new G.fn.init(J,K)}s.extend(true,G,this);G.superclass=this;G.fn=G.prototype=this();G.fn.constructor=G;G.sub=this.sub;G.fn.init=function I(J,K){if(K&&K instanceof s&&!(K instanceof G)){K=G(K)}return s.fn.init.call(this,J,K,H)};G.fn.init.prototype=G.fn;var H=G(document);h("jQuery.sub() is deprecated");return G};s.ajaxSetup({converters:{"text json":s.parseJSON}});var n=s.fn.data;s.fn.data=function(I){var H,G,J=this[0];if(J&&I==="events"&&arguments.length===1){H=s.data(J,I);
G=s._data(J,I);if((H===i||H===G)&&G!==i){h("Use of jQuery.fn.data('events') is deprecated");return G}}return n.apply(this,arguments)};var o=/\/(java|ecma)script/i,u=s.fn.andSelf||s.fn.addBack;s.fn.andSelf=function(){h("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");return u.apply(this,arguments)};if(!s.clean){s.clean=function(G,H,N,J){H=H||document;H=!H.nodeType&&H[0]||H;H=H.ownerDocument||H;h("jQuery.clean() is deprecated");var K,I,L,O,M=[];s.merge(M,s.buildFragment(G,H).childNodes);if(N){L=function(P){if(!P.type||o.test(P.type)){return J?J.push(P.parentNode?P.parentNode.removeChild(P):P):N.appendChild(P)}};for(K=0;(I=M[K])!=null;K++){if(!(s.nodeName(I,"script")&&L(I))){N.appendChild(I);if(typeof I.getElementsByTagName!=="undefined"){O=s.grep(s.merge([],I.getElementsByTagName("script")),L);M.splice.apply(M,[K+1,0].concat(O));K+=O.length}}}}return M}}var c=s.event.add,b=s.event.remove,g=s.event.trigger,r=s.fn.toggle,d=s.fn.live,m=s.fn.die,C="ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",e=new RegExp("\\b(?:"+C+")\\b"),F=/(?:^|\s)hover(\.\S+|)\b/,l=function(G){if(typeof(G)!=="string"||s.event.special.hover){return G}if(F.test(G)){h("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'")}return G&&G.replace(F,"mouseenter$1 mouseleave$1")};if(s.event.props&&s.event.props[0]!=="attrChange"){s.event.props.unshift("attrChange","attrName","relatedNode","srcElement")}if(s.event.dispatch){a(s.event,"handle",s.event.dispatch,"jQuery.event.handle is undocumented and deprecated")}s.event.add=function(J,H,I,K,G){if(J!==document&&e.test(H)){h("AJAX events should be attached to document: "+H)}c.call(this,J,l(H||""),I,K,G)};s.event.remove=function(K,I,J,G,H){b.call(this,K,l(I)||"",J,G,H)};s.fn.error=function(){var G=Array.prototype.slice.call(arguments,0);h("jQuery.fn.error() is deprecated");G.splice(0,0,"error");if(arguments.length){return this.bind.apply(this,G)}this.triggerHandler.apply(this,G);return this};s.fn.toggle=function(K,I){if(!s.isFunction(K)||!s.isFunction(I)){return r.apply(this,arguments)
}h("jQuery.fn.toggle(handler, handler...) is deprecated");var H=arguments,G=K.guid||s.guid++,J=0,L=function(M){var N=(s._data(this,"lastToggle"+K.guid)||0)%J;s._data(this,"lastToggle"+K.guid,N+1);M.preventDefault();return H[N].apply(this,arguments)||false};L.guid=G;while(J<H.length){H[J++].guid=G}return this.click(L)};s.fn.live=function(G,I,H){h("jQuery.fn.live() is deprecated");if(d){return d.apply(this,arguments)}s(this.context).on(G,this.selector,I,H);return this};s.fn.die=function(G,H){h("jQuery.fn.die() is deprecated");if(m){return m.apply(this,arguments)}s(this.context).off(G,this.selector||"**",H);return this};s.event.trigger=function(I,J,H,G){if(!H&&!e.test(I)){h("Global events are undocumented and deprecated")}return g.call(this,I,J,H||document,G)};s.each(C.split("|"),function(H,G){s.event.special[G]={setup:function(){var I=this;if(I!==document){s.event.add(document,G+"."+s.guid,function(){s.event.trigger(G,null,I,true)});s._data(this,G,s.guid++)}return false},teardown:function(){if(this!==document){s.event.remove(document,G+"."+s._data(this,G))}return false}}})})(jQuery,window);

11
apps/static/jbox/jquery.jBox-2.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,10 +4,10 @@
var checked=false;
function check_all(form) {
var checkboxes = document.getElementById(form);
if (checked == false) {
checked = true
if (checked === false) {
checked = true;
} else {
checked = false
checked = false;
}
for (var i = 0; i < checkboxes.elements.length; i++) {
if (checkboxes.elements[i].type == "checkbox") {
@ -51,13 +51,13 @@ function GetRowData(row){
//此函数用于在多选提交时至少要选择一行
function GetTableDataBox() {
var tabProduct = document.getElementById("editable");
var tableData = new Array();
var returnData = new Array();
var tableData = [];
var returnData = [];
var checkboxes = document.getElementById("contents_form");
var id_list = new Array();
var id_list = [];
len = checkboxes.elements.length;
for (var i=0; i < len; i++) {
if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked == true && checkboxes.elements[i].value != "checkall") {
if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked === true && checkboxes.elements[i].value != "checkall") {
id_list.push(i);
}
}
@ -67,7 +67,7 @@ function GetTableDataBox() {
tableData.push(GetRowData(tabProduct.rows[id_list[i]]));
}
if (id_list.length == 0){
if (id_list.length === 0){
alert('请至少选择一行!');
}
returnData.push(tableData);
@ -77,7 +77,7 @@ function GetTableDataBox() {
function move(from, to, from_o, to_o) {
$("#" + from + " option").each(function () {
if ($(this).prop("selected") == true) {
if ($(this).prop("selected") === true) {
$("#" + to).append(this);
if( typeof from_o !== 'undefined'){
$("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']"));
@ -88,7 +88,7 @@ function move(from, to, from_o, to_o) {
function move_left(from, to, from_o, to_o) {
$("#" + from + " option").each(function () {
if ($(this).prop("selected") == true) {
if ($(this).prop("selected") === true) {
$("#" + to).append(this);
if( typeof from_o !== 'undefined'){
$("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']"));
@ -124,31 +124,120 @@ function move_left(from, to, from_o, to_o) {
function selectAll(){
// 选择该页面所有option
// Select all check box
$('option').each(function(){
$(this).attr('selected', true)
})
$(this).attr('selected', true);
});
}
//
//function move_all(from, to){
// $("#"+from).children().each(function(){
// $("#"+to).append(this);
// });
//}
// function getIDall() {
// var check_array = [];
// $(".gradeX input:checked").each(function () {
// var id = $(this).attr("value");
// check_array.push(id);
// });
// return check_array.join(",");
// }
//function commit_select(form_array){
// $('#{0} option'.format(form_array)).each(function(){
// $(this).prop('selected', true)
// })
//}
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;
}
function getIDall() {
var check_array = [];
$(".gradeX input:checked").each(function () {
var id = $(this).attr("value");
check_array.push(id);
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function setAjaxCSRFToken() {
var csrftoken = getCookie('csrftoken');
var sessionid = getCookie('sessionid');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
return check_array.join(",");
}
}
function activeNav() {
var url_array = document.location.pathname.split("/");
var app = url_array[1];
var resource = url_array[2];
if (app == ''){
$('#index').addClass('active')
} else {
$("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active');
}
}
function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,}
props = props || {};
success_message = props.success_message || 'Update Successfully!';
fail_message = props.fail_message || 'Error occurred while updating.';
$.ajax({
url: props.url,
type: props.method || "PATCH",
data: props.body,
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json",
}).done(function(data, textStatue, jqXHR) {
if (typeof props.success === 'function') {
return props.success(data);
} else {
toastr.success(success_message);
}
}).fail(function(jqXHR, textStatue, errorThrown) {
if (typeof props.error === 'function') {
return props.error(errorThrown);
} else {
toastr.error(fail_message);
}
});
return true;
}
// Sweet Alert for Delete
function objectDelete(obj, name, url){
swal({
title: 'Are you sure delete ?',
text: "【" + name + "】",
type: "warning",
showCancelButton: true,
cancelButtonText: 'Cancel',
confirmButtonColor: "#DD6B55",
confirmButtonText: 'Yes, delete it!',
closeOnConfirm: false
}, function () {
$.ajax({
type : "post",
url : url,
data : {
},
dataType : "text",
success : function(data) {
swal('Deleted!' , "【"+name+"】"+"has been deleted.", "success");
$(obj).parent().parent().remove();
}
})
});
}
var jumpserver = {};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return f({type:O.error,iconClass:g().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=g()),v=e("#"+t.containerId),v.length?v:(n&&(v=c(t)),v)}function i(e,t,n){return f({type:O.info,iconClass:g().iconClasses.info,message:e,optionsOverride:n,title:t})}function o(e){w=e}function s(e,t,n){return f({type:O.success,iconClass:g().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return f({type:O.warning,iconClass:g().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e){var t=g();v||n(t),l(e,t)||u(t)}function d(t){var i=g();return v||n(i),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function u(t){for(var n=v.children(),i=n.length-1;i>=0;i--)l(e(n[i]),t)}function l(t,n){return t&&0===e(":focus",t).length?(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0):!1}function c(t){return v=e("<div/>").attr("id",t.containerId).addClass(t.positionClass).attr("aria-live","polite").attr("role","alert"),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:'<button type="button">&times;</button>',newestOnTop:!0,preventDuplicates:!1,progressBar:!1}}function m(e){w&&w(e)}function f(t){function i(t){return!e(":focus",l).length||t?(clearTimeout(O.intervalId),l[r.hideMethod]({duration:r.hideDuration,easing:r.hideEasing,complete:function(){h(l),r.onHidden&&"hidden"!==b.state&&r.onHidden(),b.state="hidden",b.endTime=new Date,m(b)}})):void 0}function o(){(r.timeOut>0||r.extendedTimeOut>0)&&(u=setTimeout(i,r.extendedTimeOut),O.maxHideTime=parseFloat(r.extendedTimeOut),O.hideEta=(new Date).getTime()+O.maxHideTime)}function s(){clearTimeout(u),O.hideEta=0,l.stop(!0,!0)[r.showMethod]({duration:r.showDuration,easing:r.showEasing})}function a(){var e=(O.hideEta-(new Date).getTime())/O.maxHideTime*100;f.width(e+"%")}var r=g(),d=t.iconClass||r.iconClass;if("undefined"!=typeof t.optionsOverride&&(r=e.extend(r,t.optionsOverride),d=t.optionsOverride.iconClass||d),r.preventDuplicates){if(t.message===C)return;C=t.message}T++,v=n(r,!0);var u=null,l=e("<div/>"),c=e("<div/>"),p=e("<div/>"),f=e("<div/>"),w=e(r.closeHtml),O={intervalId:null,hideEta:null,maxHideTime:null},b={toastId:T,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&l.addClass(r.toastClass).addClass(d),t.title&&(c.append(t.title).addClass(r.titleClass),l.append(c)),t.message&&(p.append(t.message).addClass(r.messageClass),l.append(p)),r.closeButton&&(w.addClass("toast-close-button").attr("role","button"),l.prepend(w)),r.progressBar&&(f.addClass("toast-progress"),l.prepend(f)),l.hide(),r.newestOnTop?v.prepend(l):v.append(l),l[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),r.timeOut>0&&(u=setTimeout(i,r.timeOut),O.maxHideTime=parseFloat(r.timeOut),O.hideEta=(new Date).getTime()+O.maxHideTime,r.progressBar&&(O.intervalId=setInterval(a,10))),l.hover(s,o),!r.onclick&&r.tapToDismiss&&l.click(i),r.closeButton&&w&&w.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),i(!0)}),r.onclick&&l.click(function(){r.onclick(),i()}),m(b),r.debug&&console&&console.log(b),l}function g(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),C=void 0))}var v,w,C,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:d,error:t,getContainer:n,info:i,options:{},subscribe:o,success:s,version:"2.1.0",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)});
//# sourceMappingURL=/toastr.js.map

View File

@ -1,56 +1,19 @@
{% load i18n %}
{% load static %}
<!-- Mainly scripts -->
<script src="{% static "js/plugins/metisMenu/jquery.metisMenu.js" %}"></script>
<!-- Custom and plugin javascript -->
<script src="{% static "js/plugins/toastr/toastr.min.js" %}"></script>
<script src="{% static "js/inspinia.js" %}"></script>
<script src="{% static "js/jumpserver.js" %}"></script>
<script>
<!-- active menu -->
var url_array = document.location.pathname.split("/");
app = url_array[1];
resource = url_array[2];
if (app == ''){
$('#index').addClass('active')
} else {
$("#"+app).addClass('active');
$('#'+app+' #'+resource).addClass('active');
}
activeNav();
$(document).ready(function(){
setAjaxCSRFToken();
<!-- ajax set cookie -->
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;
}
var csrftoken = getCookie('csrftoken');
var sessionid = getCookie('sessionid');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
// Set textarea rows five
$('textarea').attr('rows', 5);
});
<!-- textarea rows -->
$('textarea').attr('rows', 5)
</script>
</script>

View File

@ -3,13 +3,26 @@
<!-- css file -->
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
<link href="{% static "css/font-awesome.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/toastr/toastr.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<link href="{% static "css/style.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/vaildator/jquery.validator.css" %}" rel="stylesheet">
<!-- scripts -->
<script src="{% static "js/jquery-2.1.1.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
<!-- Jbox -->
<link href="{% static 'jbox/Skins2/jumpserver/jbox.css' %}" rel="stylesheet">
<script src="{% static 'jbox/jquery-migrate-1.1.1.min.js' %}"></script>
<script src="{% static 'jbox/jquery.jBox-2.3.min.js' %}"></script>
<script src="{% static 'jbox/i18n/jquery.jBox-zh-CN.js' %}"></script>
<!-- Sweet alert -->
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>

View File

@ -19,9 +19,15 @@
</a>
</li>
<li>
{% if user.is_authenticated %}
<a href="{% url 'users:logout' %}">
<i class="fa fa-sign-out"></i> Log out
<i class="fa fa-sign-out"></i>{% trans 'Logout' %}
</a>
{% else %}
<a href="{% url 'users:login' %}">
<i class="fa fa-sign-in"></i>{% trans 'Login' %}
</a>
{% endif %}
</li>
</ul>
</nav>

View File

@ -22,7 +22,6 @@
<div class="ibox-content">
<div class="">
{# left button add #}
{% block content_left_head %} {% endblock %}
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">

View File

@ -0,0 +1,20 @@
{% load i18n %}
<div aria-hidden="true" role="dialog" tabindex="-1" id="{% block modal_id %}{% endblock %}" class="modal inmodal" style="display: none;">
<div class="modal-dialog">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button data-dismiss="modal" class="close" type="button"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">{% block modal_title %}{% endblock %}</h4>
<small>{% block modal_comment %}{% endblock %}</small>
</div>
<div class="modal-body">
{% block modal_body %}
{% endblock %}
</div>
<div class="modal-footer">
<button data-dismiss="modal" class="btn btn-white" type="button">{% trans "Close" %}</button>
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}{% endblock %}">{% trans 'Confirm' %}</button>
</div>
</div>
</div>
</div>

View File

@ -20,21 +20,21 @@
<ul class="nav nav-second-level">
<li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset' %}</a></li>
<li id="asset-group"><a href="{% url 'assets:asset-group-list' %}">{% trans 'Asset group' %}</a></li>
<li id="idc"><a href="">{% trans 'IDC' %}</a></li>
<li id="admin-user"><a href="">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="">{% trans 'System user' %}</a></li>
<li id="idc"><a href="{% url 'assets:idc-list' %}">{% trans 'IDC' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id=""><a href="">{% trans 'Label' %}</a></li>
</ul>
</li>
<li id="">
<li id="perms">
<a href="#"><i class="fa fa-edit"></i> <span class="nav-label">{% trans 'Perms' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li id="sudo">
<a class="sudo" href="">{% trans 'Perm' %}</a>
</li>
<li id="role">
<a href="">{% trans 'Create perm' %}</a>
<li id="asset-permission">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Asset permission' %}</a>
</li>
{# <li id="user-group">#}
{# <a href="">{% trans 'User group perm' %}</a>#}
{# </li>#}
</ul>
</li>
<li id="">

View File

@ -34,11 +34,6 @@
</div>
{% endif %}
<script>
{# function sleep(n) { //n表示的毫秒数#}
{# var start = new Date().getTime();#}
{# while (true) if (new Date().getTime() - start > n) break;#}
{# }#}
$(document).ready(function () {
$('.page').click(function () {
var searchStr = location.search;

View File

@ -8,10 +8,10 @@
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="clear">
<span class="block m-t-xs">
<strong class="font-bold"> {{ request.user.name }}<span style="color: #8095a8"></span></strong>
<strong class="font-bold"> {{ user.name }}<span style="color: #8095a8"></span></strong>
</span>
<span class="text-muted text-xs block">
{{ request.user.get_role_display | default:_('User') }}<b class="caret"></b>
{{ user.get_role_display | default:_('User') }}<b class="caret"></b>
</span>
</span>
</a>

View File

@ -12,7 +12,6 @@
{% include '_head_css_js.html' %}
{% block custom_head_css_js %} {% endblock %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
<script src="{% static "js/jumpserver.js" %}"></script>
</head>
<body>

View File

@ -0,0 +1,285 @@
{% load staticfiles %}
{% load i18n %}
{% load rest_framework %}
<!DOCTYPE html>
<html>
<head>
{% block head %}
{% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="robots" content="NONE,NOARCHIVE" />
{% endblock %}
<title>{% block title %}{% if name %}{{ name }} {% endif %}Django REST framework{% endblock %}</title>
{% block style %}
{% block bootstrap_theme %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
{% endblock %}
{% endblock %}
</head>
{% block body %}
<body class="{% block bodyclass %}{% endblock %}">
<div class="wrapper">
{% block navbar %}
<div class="navbar navbar-static-top {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="container">
<span>
{% block branding %}
<a class='navbar-brand' rel="nofollow" href='http://www.django-rest-framework.org'>
Django REST framework
</a>
{% endblock %}
</span>
<ul class="nav navbar-nav pull-right">
{% block userlinks %}
{% if user.is_authenticated %}
{% optional_logout request user %}
{% else %}
{% optional_login request %}
{% endif %}
{% endblock %}
</ul>
</div>
</div>
{% endblock %}
<div class="container">
{% block breadcrumbs %}
<ul class="breadcrumb">
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
{% if forloop.last %}
<li class="active"><a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a></li>
{% else %}
<li><a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endblock %}
<!-- Content -->
<div id="content">
{% if 'GET' in allowed_methods %}
<form id="get-form" class="pull-right">
<fieldset>
{% if api_settings.URL_FORMAT_OVERRIDE %}
<div class="btn-group format-selection">
<a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
<button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" title="Specify a format for the GET request">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% for format in available_formats %}
<li>
<a class="js-tooltip format-option" href="{% add_query_param request api_settings.URL_FORMAT_OVERRIDE format %}" rel="nofollow" title="Make a GET request on the {{ name }} resource with the format set to `{{ format }}`">{{ format }}</a>
</li>
{% endfor %}
</ul>
</div>
{% else %}
<a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
{% endif %}
</fieldset>
</form>
{% endif %}
{% if options_form %}
<form class="button-form" action="{{ request.get_full_path }}" data-method="OPTIONS">
<button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
</form>
{% endif %}
{% if delete_form %}
<button class="btn btn-danger button-form js-tooltip" title="Make a DELETE request on the {{ name }} resource" data-toggle="modal" data-target="#deleteModal">DELETE</button>
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<h4 class="text-center">Are you sure you want to delete this {{ name }}?</h4>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<form class="button-form" action="{{ request.get_full_path }}" data-method="DELETE">
<button class="btn btn-danger">Delete</button>
</form>
</div>
</div>
</div>
</div>
{% endif %}
{% if filter_form %}
<button style="float: right; margin-right: 10px" data-toggle="modal" data-target="#filtersModal" class="btn btn-default">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
{% trans "Filters" %}
</button>
{% endif %}
<div class="content-main">
<div class="page-header">
<h1>{{ name }}</h1>
</div>
<div style="float:left">
{% block description %}
{{ description }}
{% endblock %}
</div>
{% if paginator %}
<nav style="float: right">
{% get_pagination_html paginator %}
</nav>
{% endif %}
<div class="request-info" style="clear: both" >
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
</div>
<div class="response-info">
<pre class="prettyprint"><span class="meta nocode"><b>HTTP {{ response.status_code }} {{ response.status_text }}</b>{% autoescape off %}
{% for key, val in response_headers.items %}<b>{{ key }}:</b> <span class="lit">{{ val|break_long_headers|urlize_quoted_links }}</span>
{% endfor %}
</span>{{ content|urlize_quoted_links }}</pre>{% endautoescape %}
</div>
</div>
{% if display_edit_forms %}
{% if post_form or raw_data_post_form %}
<div {% if post_form %}class="tabbable"{% endif %}>
{% if post_form %}
<ul class="nav nav-tabs form-switcher">
<li>
<a name='html-tab' href="#post-object-form" data-toggle="tab">HTML form</a>
</li>
<li>
<a name='raw-tab' href="#post-generic-content-form" data-toggle="tab">Raw data</a>
</li>
</ul>
{% endif %}
<div class="well tab-content">
{% if post_form %}
<div class="tab-pane" id="post-object-form">
{% with form=post_form %}
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal" novalidate>
<fieldset>
{% csrf_token %}
{{ post_form }}
<div class="form-actions">
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
</div>
</fieldset>
</form>
{% endwith %}
</div>
{% endif %}
<div {% if post_form %}class="tab-pane"{% endif %} id="post-generic-content-form">
{% with form=raw_data_post_form %}
<form action="{{ request.get_full_path }}" method="POST" class="form-horizontal">
<fieldset>
{% include "rest_framework/raw_data_form.html" %}
<div class="form-actions">
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
</div>
</fieldset>
</form>
{% endwith %}
</div>
</div>
</div>
{% endif %}
{% if put_form or raw_data_put_form or raw_data_patch_form %}
<div {% if put_form %}class="tabbable"{% endif %}>
{% if put_form %}
<ul class="nav nav-tabs form-switcher">
<li>
<a name='html-tab' href="#put-object-form" data-toggle="tab">HTML form</a>
</li>
<li>
<a name='raw-tab' href="#put-generic-content-form" data-toggle="tab">Raw data</a>
</li>
</ul>
{% endif %}
<div class="well tab-content">
{% if put_form %}
<div class="tab-pane" id="put-object-form">
<form action="{{ request.get_full_path }}" data-method="PUT" enctype="multipart/form-data" class="form-horizontal" novalidate>
<fieldset>
{{ put_form }}
<div class="form-actions">
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
</div>
</fieldset>
</form>
</div>
{% endif %}
<div {% if put_form %}class="tab-pane"{% endif %} id="put-generic-content-form">
{% with form=raw_data_put_or_patch_form %}
<form action="{{ request.get_full_path }}" data-method="PUT" class="form-horizontal">
<fieldset>
{% include "rest_framework/raw_data_form.html" %}
<div class="form-actions">
{% if raw_data_put_form %}
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
{% endif %}
{% if raw_data_patch_form %}
<button data-method="PATCH" class="btn btn-primary js-tooltip" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
{% endif %}
</div>
</fieldset>
</form>
{% endwith %}
</div>
</div>
</div>
{% endif %}
{% endif %}
</div><!-- /.content -->
</div><!-- /.container -->
</div><!-- ./wrapper -->
{% if filter_form %}
{{ filter_form }}
{% endif %}
{% block script %}
<script>
window.drf = {
csrfHeaderName: "{{ csrf_header_name|default:'X-CSRFToken' }}",
csrfCookieName: "{{ csrf_cookie_name|default:'csrftoken' }}"
};
</script>
<script src="{% static "rest_framework/js/jquery-1.12.4.min.js" %}"></script>
<script src="{% static "rest_framework/js/ajax-form.js" %}"></script>
<script src="{% static "rest_framework/js/csrf.js" %}"></script>
<script src="{% static "rest_framework/js/bootstrap.min.js" %}"></script>
<script src="{% static "rest_framework/js/prettify-min.js" %}"></script>
<script src="{% static "rest_framework/js/default.js" %}"></script>
<script>
$(document).ready(function() {
$('form').ajaxForm();
});
</script>
{% endblock %}
</body>
{% endblock %}
</html>

View File

@ -3,11 +3,9 @@
import logging
from rest_framework import generics, mixins, status, permissions
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import generics
from .serializers import UserSerializer, UserGroupSerializer, UserActiveSerializer
from .serializers import UserSerializer, UserGroupSerializer, UserAttributeSerializer, UserGroupEditSerializer
from .models import User, UserGroup
@ -33,16 +31,6 @@ class UserDetailDeleteUpdateApi(generics.RetrieveUpdateDestroyAPIView):
# return super(UserDetailDeleteUpdateApi, self).get(request, *args, **kwargs)
class UserActiveApi(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserActiveSerializer
# def put(self, request, *args, **kwargs):
# for k, v in request.META.items():
# logger.debug("%s --> %s" % (k, v))
# return super(UserActiveApi, self).put(request, *args, **kwargs)
class UserGroupListAddApi(generics.ListCreateAPIView):
queryset = UserGroup.objects.all()
serializer_class = UserGroupSerializer
@ -52,3 +40,39 @@ class UserGroupDetailDeleteUpdateApi(generics.RetrieveUpdateDestroyAPIView):
queryset = UserGroup.objects.all()
serializer_class = UserGroupSerializer
class UserAttributeApi(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserAttributeSerializer
class UserGroupEditApi(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserGroupEditSerializer
class UserResetPasswordApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserGroupEditSerializer
def perform_update(self, serializer):
# Note: we are not updating the user object here.
# We just do the reset-password staff.
user = self.get_object()
import uuid
user.password_raw = str(uuid.uuid4())
user.save()
from .utils import send_reset_password_mail
send_reset_password_mail(user)
class UserResetPKApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserGroupEditSerializer
def perform_update(self, serializer):
user = self.get_object()
user._public_key = ''
user.save()
from .utils import send_reset_ssh_key_mail
send_reset_ssh_key_mail(user)

View File

@ -1,20 +1,24 @@
# ~*~ coding: utf-8 ~*~
from django.forms import ModelForm
from django import forms
from captcha.fields import CaptchaField
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField
from .models import User, UserGroup
class UserLoginForm(forms.Form):
class UserLoginForm(AuthenticationForm):
username = forms.CharField(label=_('Username'), max_length=100)
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput, max_length=100)
password = forms.CharField(
label=_('Password'), widget=forms.PasswordInput, max_length=100,
strip=False)
captcha = CaptchaField()
class UserCreateForm(ModelForm):
class UserCreateForm(forms.ModelForm):
class Meta:
model = User
fields = [
@ -32,7 +36,8 @@ class UserCreateForm(ModelForm):
}
class UserUpdateForm(ModelForm):
class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = [
@ -47,11 +52,12 @@ class UserUpdateForm(ModelForm):
}
widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join usergroups')}),
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
}
class UserGroupForm(ModelForm):
class UserGroupForm(forms.ModelForm):
class Meta:
model = UserGroup
@ -62,3 +68,33 @@ class UserGroupForm(ModelForm):
help_texts = {
'name': '* required'
}
class UserInfoForm(forms.Form):
name = forms.CharField(max_length=20, label=_('name'))
avatar = forms.ImageField(label=_('avatar'), required=False)
wechat = forms.CharField(max_length=30, label=_('wechat'), required=False)
phone = forms.CharField(max_length=20, label=_('phone'), required=False)
enable_otp = forms.BooleanField(required=False, label=_('enable otp'))
class UserKeyForm(forms.Form):
public_key = forms.CharField(
label=_('ssh public key'), max_length=5000,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
help_text=_('Paste your id_ras.pub here.'))
def clean_public_key(self):
from sshpubkeys import SSHKey
from sshpubkeys.exceptions import InvalidKeyException
public_key = self.cleaned_data['public_key']
ssh = SSHKey(public_key)
try:
ssh.parse()
except InvalidKeyException as e:
print e
raise forms.ValidationError(_('Not a valid ssh public key'))
except NotImplementedError as e:
print e
raise forms.ValidationError(_('Not a valid ssh public key'))
return public_key

14
apps/users/hands.py Normal file
View File

@ -0,0 +1,14 @@
"""
jumpserver.__app__.hands.py
~~~~~~~~~~~~~~~~~
This app depends other apps api, function .. should be import or write mack here.
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2016 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""

View File

@ -2,71 +2,36 @@
from __future__ import unicode_literals
import datetime
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.utils import timezone
from django.db import models
from django.contrib.auth.models import AbstractUser, Permission
from django.contrib.auth.models import AbstractUser
from django.core import signing
from django.db import models, IntegrityError
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import IntegrityError
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from rest_framework.authtoken.models import Token
from django.core import signing
# class Role(models.Model):
# name = models.CharField('name', max_length=80, unique=True)
# permissions = models.ManyToManyField(
# Permission,
# verbose_name='permissions',
# blank=True,
# )
# date_added = models.DateTimeField(auto_now_add=True)
# created_by = models.CharField(max_length=100)
# comment = models.CharField(max_length=80, blank=True)
#
# def __unicode__(self):
# return self.name
#
# def delete(self, using=None, keep_parents=False):
# if self.users.all().count() > 0:
# raise OperationalError('Role %s has some member, should not be delete.' % self.name)
# else:
# return super(Role, self).delete(using=using, keep_parents=keep_parents)
#
# class Meta:
# db_table = 'role'
#
# @classmethod
# def initial(cls):
# roles = {
# 'Administrator': {'permissions': Permission.objects.all(), 'comment': '管理员'},
# 'User': {'permissions': [], 'comment': '用户'},
# 'Auditor': {'permissions': Permission.objects.filter(content_type__app_label='audits'),
# 'comment': '审计员'},
# }
# for role_name, props in roles.items():
# if not cls.objects.filter(name=role_name):
# role = cls.objects.create(name=role_name, comment=props.get('comment', ''), created_by='System')
# if props.get('permissions'):
# role.permissions = props.get('permissions')
from common.utils import encrypt, decrypt, date_expired_default
class UserGroup(models.Model):
name = models.CharField(max_length=100, unique=True, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_added = models.DateTimeField(auto_now_add=True)
date_created = models.DateTimeField(auto_now_add=True)
created_by = models.CharField(max_length=100)
def __unicode__(self):
return self.name
def has_member(self, user):
if user in self.users.all():
return True
return False
class Meta:
db_table = 'user-group'
db_table = 'user_group'
@classmethod
def initial(cls):
@ -76,16 +41,15 @@ class UserGroup(models.Model):
@classmethod
def generate_fake(cls, count=100):
from random import seed, randint, choice
from random import seed, choice
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
group = cls(name=forgery_py.name.full_name(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by=choice(User.objects.all()).username
)
)
try:
group.save()
except IntegrityError:
@ -93,10 +57,6 @@ class UserGroup(models.Model):
continue
def date_expired_default():
return timezone.now() + timezone.timedelta(days=365 * 70)
class User(AbstractUser):
ROLE_CHOICES = (
('Admin', _('Administrator')),
@ -106,17 +66,17 @@ class User(AbstractUser):
username = models.CharField(max_length=20, unique=True, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
email = models.EmailField(max_length=30, unique=True, verbose_name=_('Email'))
groups = models.ManyToManyField(UserGroup, related_name='users', blank=True, verbose_name=_('Usergroup'))
groups = models.ManyToManyField(UserGroup, related_name='users', blank=True, verbose_name=_('User group'))
role = models.CharField(choices=ROLE_CHOICES, default='User', max_length=10, blank=True, verbose_name=_('Role'))
avatar = models.ImageField(upload_to="avatar", verbose_name=_('Avatar'))
wechat = models.CharField(max_length=30, blank=True, verbose_name=_('Wechat'))
phone = models.CharField(max_length=20, blank=True, verbose_name=_('Phone'))
enable_otp = models.BooleanField(default=False, verbose_name=_('Enable OTP'))
secret_key_otp = models.CharField(max_length=16, blank=True)
private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh private key'))
public_key = models.CharField(max_length=1000, blank=True, verbose_name=_('ssh public key'))
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh private key'))
_public_key = models.CharField(max_length=1000, blank=True, verbose_name=_('ssh public key'))
comment = models.TextField(max_length=200, blank=True, verbose_name=_('Comment'))
is_first_login = models.BooleanField(default=False)
is_first_login = models.BooleanField(default=True)
date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True,
verbose_name=_('Date expired'))
created_by = models.CharField(max_length=30, default='', verbose_name=_('Created by'))
@ -131,8 +91,8 @@ class User(AbstractUser):
#: user = User(username='example', ...)
#: user.set_password('password')
@password_raw.setter
def password_raw(self, raw_password):
self.set_password(raw_password)
def password_raw(self, password_raw_):
self.set_password(password_raw_)
@property
def is_expired(self):
@ -141,6 +101,22 @@ class User(AbstractUser):
else:
return True
@property
def private_key(self):
return decrypt(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = encrypt(private_key_raw)
@property
def public_key(self):
return decrypt(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = encrypt(public_key_raw)
@property
def is_superuser(self):
if self.role == 'Admin':
@ -198,6 +174,11 @@ class User(AbstractUser):
def generate_reset_token(self):
return signing.dumps({'reset': self.id, 'email': self.email})
def is_member_of(self, user_group):
if user_group in self.groups.all():
return True
return False
@classmethod
def validate_reset_token(cls, token, max_age=3600):
try:
@ -246,7 +227,7 @@ class User(AbstractUser):
wechat=forgery_py.internet.user_name(True),
comment=forgery_py.lorem_ipsum.sentence(),
created_by=choice(cls.objects.all()).username,
)
)
try:
user.save()
except IntegrityError:
@ -258,13 +239,13 @@ class User(AbstractUser):
def init_all_models():
for model in (UserGroup, User):
if hasattr(model, 'initial'):
if hasattr(model, b'initial'):
model.initial()
def generate_fake():
for model in (UserGroup, User):
if hasattr(model, 'generate_fake'):
if hasattr(model, b'generate_fake'):
model.generate_fake()
@ -275,4 +256,3 @@ def create_auth_token(sender, instance=None, created=False, **kwargs):
Token.objects.create(user=instance)
except IntegrityError:
pass

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from .models import User, UserGroup
@ -17,15 +19,38 @@ class UserSerializer(serializers.ModelSerializer):
]
class UserActiveSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['is_active']
class UserGroupSerializer(serializers.ModelSerializer):
users = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='users:user-detail-api')
class Meta:
model = UserGroup
fields = '__all__'
class UserAttributeSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['avatar', 'wechat', 'phone', 'enable_otp', 'comment', 'is_active', 'name']
class UserGroupEditSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
class Meta:
model = User
fields = ['id', 'groups']
class UserPKUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', '_private_key']
def validate__private_key(self, value):
from users.utils import validate_ssh_pk
checked, reason = validate_ssh_pk(value)
if not checked:
raise serializers.ValidationError(_('Not a valid ssh private key.'))
return value

Some files were not shown because too many files have changed in this diff Show More