Commit 0427d406 authored by ibuler's avatar ibuler

Add init data file

parent 96dd2f5c
...@@ -13,14 +13,6 @@ def join_queryset_attr(queryset, attr, delimiter=', '): ...@@ -13,14 +13,6 @@ def join_queryset_attr(queryset, attr, delimiter=', '):
return delimiter.join([getattr(obj, attr, '') for obj in queryset]) return delimiter.join([getattr(obj, attr, '') for obj in queryset])
@register.filter
def is_expired(datetime):
if datetime > timezone.now():
return False
else:
return True
@register.filter @register.filter
def pagination_range(total_page, current_num=1, display=5): def pagination_range(total_page, current_num=1, display=5):
"""Return Page range """Return Page range
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
...@@ -148,13 +148,22 @@ USE_TZ = True ...@@ -148,13 +148,22 @@ USE_TZ = True
# https://docs.djangoproject.com/en/1.10/howto/static-files/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATICFILES_DIRS = ( STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"), os.path.join(BASE_DIR, "static"),
) )
# Media files (File, ImageField) will be save these
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/') + '/'
# Custom User Auth model
AUTH_USER_MODEL = 'users.User' AUTH_USER_MODEL = 'users.User'
# Use django-bootstrap-form to format template, input max width arg
BOOTSTRAP_COLUMN_COUNT = 11 BOOTSTRAP_COLUMN_COUNT = 11
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/') + '/'
...@@ -5,3 +5,4 @@ from django.apps import AppConfig ...@@ -5,3 +5,4 @@ from django.apps import AppConfig
class UsersConfig(AppConfig): class UsersConfig(AppConfig):
name = 'users' name = 'users'
[{"model": "users.role", "pk": 1, "fields": {"name": "Administrator", "date_added": "2016-08-20T17:03:42.631Z", "created_by": "System", "comment": "\u7ba1\u7406\u5458", "permissions": [16, 17, 18, 19, 20, 21, 10, 11, 12, 13, 14, 15, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 1, 2, 3, 4, 5, 6, 7, 8, 9]}}, {"model": "users.role", "pk": 2, "fields": {"name": "User", "date_added": "2016-08-20T17:03:42.671Z", "created_by": "System", "comment": "\u7528\u6237", "permissions": []}}, {"model": "users.role", "pk": 3, "fields": {"name": "Auditor", "date_added": "2016-08-20T17:03:42.683Z", "created_by": "System", "comment": "\u5ba1\u8ba1\u5458", "permissions": []}}, {"model": "users.usergroup", "pk": 1, "fields": {"name": "ALL", "comment": "Default usergroup for all user", "date_added": "2016-08-20T17:03:42.693Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$xZUhPadgI8rs$n2rm5futcOv7Ww4b4BflN8K90Vk3u7ozfnOS7GQq0ns=", "last_login": null, "is_superuser": false, "first_name": "", "last_name": "", "is_staff": false, "is_active": true, "date_joined": "2016-08-20T17:03:42.752Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "role": 1, "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "date_expired": "2086-08-03T17:03:42.753Z", "created_by": "System", "user_permissions": [], "groups": [1]}}]
\ No newline at end of file
[{"model": "users.role", "pk": 1, "fields": {"name": "Administrator", "date_added": "2016-08-20T17:03:42.631Z", "created_by": "System", "comment": "\u7ba1\u7406\u5458", "permissions": [16, 17, 18, 19, 20, 21, 10, 11, 12, 13, 14, 15, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 1, 2, 3, 4, 5, 6, 7, 8, 9]}}, {"model": "users.role", "pk": 2, "fields": {"name": "User", "date_added": "2016-08-20T17:03:42.671Z", "created_by": "System", "comment": "\u7528\u6237", "permissions": []}}, {"model": "users.role", "pk": 3, "fields": {"name": "Auditor", "date_added": "2016-08-20T17:03:42.683Z", "created_by": "System", "comment": "\u5ba1\u8ba1\u5458", "permissions": []}}, {"model": "users.usergroup", "pk": 1, "fields": {"name": "ALL", "comment": "Default usergroup for all user", "date_added": "2016-08-20T17:03:42.693Z", "created_by": "System"}}, {"model": "users.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$30000$xZUhPadgI8rs$n2rm5futcOv7Ww4b4BflN8K90Vk3u7ozfnOS7GQq0ns=", "last_login": null, "is_superuser": false, "first_name": "", "last_name": "", "is_staff": false, "is_active": true, "date_joined": "2016-08-20T17:03:42.752Z", "username": "admin", "name": "Administrator", "email": "admin@jumpserver.org", "avatar": "", "wechat": "", "phone": "", "enable_otp": false, "secret_key_otp": "", "role": 1, "private_key": "", "public_key": "", "comment": "Administrator is the super user of system", "date_expired": "2086-08-03T17:03:42.753Z", "created_by": "System", "user_permissions": [], "groups": [1]}}]
\ No newline at end of file
...@@ -11,7 +11,7 @@ class UserAddForm(ModelForm): ...@@ -11,7 +11,7 @@ class UserAddForm(ModelForm):
model = User model = User
fields = [ fields = [
'username', 'name', 'email', 'groups', 'wechat', 'username', 'name', 'email', 'groups', 'wechat',
'phone', 'enable_2FA', 'role', 'date_expired', 'comment', 'phone', 'enable_otp', 'role', 'date_expired', 'comment',
] ]
help_texts = { help_texts = {
...@@ -31,7 +31,7 @@ class UserUpdateForm(ModelForm): ...@@ -31,7 +31,7 @@ class UserUpdateForm(ModelForm):
model = User model = User
fields = [ fields = [
'name', 'email', 'groups', 'wechat', 'avatar', 'name', 'email', 'groups', 'wechat', 'avatar',
'phone', 'enable_2FA', 'role', 'date_expired', 'comment', 'phone', 'enable_otp', 'role', 'date_expired', 'comment',
] ]
help_texts = { help_texts = {
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-13 16:36 # Generated by Django 1.10 on 2016-08-20 16:21
from __future__ import unicode_literals from __future__ import unicode_literals
import django.contrib.auth.models import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import users.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
...@@ -25,22 +25,24 @@ class Migration(migrations.Migration): ...@@ -25,22 +25,24 @@ class Migration(migrations.Migration):
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')), ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('avatar', models.ImageField(default='', upload_to=b'', verbose_name='\u5934\u50cf')), ('username', models.CharField(max_length=20, unique=True, verbose_name='\u7528\u6237\u540d')),
('wechat', models.CharField(max_length=30, verbose_name='\u5fae\u4fe1')), ('name', models.CharField(help_text='* required', max_length=20, verbose_name='\u59d3\u540d')),
('phone', models.CharField(max_length=20, verbose_name='\u624b\u673a')), ('email', models.EmailField(help_text='* required', max_length=30, unique=True, verbose_name='\u90ae\u4ef6')),
('enable_2FA', models.BooleanField(default=False, verbose_name='\u542f\u7528\u4e8c\u6b21\u9a8c\u8bc1')), ('avatar', models.ImageField(upload_to='avatar', verbose_name='\u5934\u50cf')),
('secret_key_2FA', models.CharField(max_length=16)), ('wechat', models.CharField(blank=True, max_length=30, verbose_name='\u5fae\u4fe1')),
('private_key', models.CharField(max_length=5000)), ('phone', models.CharField(blank=True, max_length=20, verbose_name='\u624b\u673a\u53f7')),
('public_key', models.CharField(max_length=1000)), ('enable_otp', models.BooleanField(default=False, verbose_name='\u542f\u7528\u4e8c\u6b21\u9a8c\u8bc1')),
('created_by', models.CharField(max_length=30)), ('secret_key_otp', models.CharField(blank=True, max_length=16)),
('date_expired', models.DateTimeField()), ('private_key', models.CharField(blank=True, max_length=5000, verbose_name='ssh\u79c1\u94a5')),
('public_key', models.CharField(blank=True, max_length=1000, verbose_name='\u516c\u94a5')),
('comment', models.TextField(blank=True, max_length=200, verbose_name='\u63cf\u8ff0')),
('date_expired', models.DateTimeField(default=users.models.date_expired_default, verbose_name='\u6709\u6548\u671f')),
('created_by', models.CharField(default='', max_length=30)),
], ],
options={ options={
'db_table': 'user', 'db_table': 'user',
...@@ -52,20 +54,21 @@ class Migration(migrations.Migration): ...@@ -52,20 +54,21 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Role', name='Role',
fields=[ fields=[
('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.Group')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=80, unique=True, verbose_name='name')),
('comment', models.CharField(blank=True, max_length=80)), ('comment', models.CharField(blank=True, max_length=80)),
('permissions', models.ManyToManyField(blank=True, to='auth.Permission', verbose_name='permissions')),
], ],
options={ options={
'db_table': 'role', 'db_table': 'role',
}, },
bases=('auth.group',),
), ),
migrations.CreateModel( migrations.CreateModel(
name='UserGroup', name='UserGroup',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='\u8bf7\u8f93\u5165\u7ec4\u540d\u79f0', max_length=100, unique=True, verbose_name='\u7ec4\u540d\u79f0')), ('name', models.CharField(max_length=100, unique=True, verbose_name='\u7ec4\u540d\u79f0')),
('comment', models.TextField(blank=True, help_text='\u8bf7\u8f93\u5165\u7528\u6237\u7ec4\u63cf\u8ff0', verbose_name='\u63cf\u8ff0')), ('comment', models.TextField(blank=True, verbose_name='\u63cf\u8ff0')),
('date_added', models.DateTimeField(auto_now_add=True)), ('date_added', models.DateTimeField(auto_now_add=True)),
('created_by', models.CharField(max_length=100)), ('created_by', models.CharField(max_length=100)),
], ],
...@@ -76,12 +79,12 @@ class Migration(migrations.Migration): ...@@ -76,12 +79,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='groups', name='groups',
field=models.ManyToManyField(to='users.UserGroup'), field=models.ManyToManyField(help_text='* required', to='users.UserGroup', verbose_name='\u7528\u6237\u7ec4'),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='role', name='role',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.Role'), field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.Role', verbose_name='\u89d2\u8272'),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-20 16:51
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='role',
name='created_by',
field=models.CharField(default='System', max_length=100),
preserve_default=False,
),
migrations.AddField(
model_name='role',
name='date_added',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-20 22:43
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0009_auto_20160818_1444'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_expired',
field=models.DateTimeField(default=datetime.datetime(2086, 8, 3, 22, 43, 39, 338972), verbose_name='\u6709\u6548\u671f'),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(max_length=20, unique=True, verbose_name='\u7528\u6237\u540d'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-20 15:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0010_auto_20160820_2243'),
]
operations = [
migrations.RenameField(
model_name='user',
old_name='enable_2FA',
new_name='enable_otp',
),
migrations.RenameField(
model_name='user',
old_name='secret_key_2FA',
new_name='secret_key_otp',
),
migrations.AlterField(
model_name='user',
name='date_expired',
field=models.DateTimeField(verbose_name='\u6709\u6548\u671f'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-20 15:13
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('users', '0011_auto_20160820_2312'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_expired',
field=models.DateTimeField(default=datetime.datetime(2086, 8, 3, 15, 13, 28, 819421, tzinfo=utc), verbose_name='\u6709\u6548\u671f'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-20 15:13
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('users', '0012_auto_20160820_2313'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_expired',
field=models.DateTimeField(default=datetime.datetime(2086, 8, 3, 15, 13, 45, 910700, tzinfo=utc), verbose_name='\u6709\u6548\u671f'),
),
]
...@@ -4,15 +4,21 @@ from __future__ import unicode_literals ...@@ -4,15 +4,21 @@ from __future__ import unicode_literals
import datetime import datetime
from django.db.models.signals import post_migrate from django.contrib.auth.hashers import make_password
from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser, Permission from django.contrib.auth.models import AbstractUser, Permission
from django.contrib.auth.models import Group as AbstractGroup
class Role(AbstractGroup): 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) comment = models.CharField(max_length=80, blank=True)
def __unicode__(self): def __unicode__(self):
...@@ -21,9 +27,6 @@ class Role(AbstractGroup): ...@@ -21,9 +27,6 @@ class Role(AbstractGroup):
class Meta: class Meta:
db_table = 'role' db_table = 'role'
def init(self):
pass
@classmethod @classmethod
def init(cls): def init(cls):
roles = { roles = {
...@@ -33,14 +36,11 @@ class Role(AbstractGroup): ...@@ -33,14 +36,11 @@ class Role(AbstractGroup):
'comment': '审计员'}, 'comment': '审计员'},
} }
for role in cls.objects.all():
role.permissions.clear()
cls.objects.all().delete()
for role_name, props in roles.items(): for role_name, props in roles.items():
role = cls.objects.create(name=role_name, comment=props.get('comment', '')) if not cls.objects.filter(name=role_name):
role.permissions = props.get('permissions', []) role = cls.objects.create(name=role_name, comment=props.get('comment', ''), created_by='System')
if props.get('permissions'):
role.permissions = props.get('permissions')
class UserGroup(models.Model): class UserGroup(models.Model):
...@@ -58,7 +58,7 @@ class UserGroup(models.Model): ...@@ -58,7 +58,7 @@ class UserGroup(models.Model):
@classmethod @classmethod
def init(cls): def init(cls):
if not cls.objects.all(): if not cls.objects.all():
group = cls(name='所有人', comment='所有人默认都在用户组', created_by='System') group = cls(name='ALL', comment='Default usergroup for all user', created_by='System')
group.save() group.save()
@classmethod @classmethod
...@@ -80,6 +80,10 @@ class UserGroup(models.Model): ...@@ -80,6 +80,10 @@ class UserGroup(models.Model):
continue continue
def date_expired_default():
return timezone.now() + timezone.timedelta(days=365 * 70)
class User(AbstractUser): class User(AbstractUser):
username = models.CharField(max_length=20, unique=True, verbose_name='用户名') username = models.CharField(max_length=20, unique=True, verbose_name='用户名')
name = models.CharField(max_length=20, verbose_name='姓名', help_text='* required') name = models.CharField(max_length=20, verbose_name='姓名', help_text='* required')
...@@ -88,23 +92,40 @@ class User(AbstractUser): ...@@ -88,23 +92,40 @@ class User(AbstractUser):
avatar = models.ImageField(upload_to="avatar", verbose_name='头像') avatar = models.ImageField(upload_to="avatar", verbose_name='头像')
wechat = models.CharField(max_length=30, blank=True, verbose_name='微信') wechat = models.CharField(max_length=30, blank=True, verbose_name='微信')
phone = models.CharField(max_length=20, blank=True, verbose_name='手机号') phone = models.CharField(max_length=20, blank=True, verbose_name='手机号')
enable_2FA = models.BooleanField(default=False, verbose_name='启用二次验证') enable_otp = models.BooleanField(default=False, verbose_name='启用二次验证')
secret_key_2FA = models.CharField(max_length=16, blank=True) secret_key_otp = models.CharField(max_length=16, blank=True)
role = models.ForeignKey(Role, on_delete=models.PROTECT, verbose_name='角色') role = models.ForeignKey(Role, on_delete=models.PROTECT, verbose_name='角色')
private_key = models.CharField(max_length=5000, blank=True, verbose_name='ssh私钥') # ssh key max length 4096 bit private_key = models.CharField(max_length=5000, blank=True, verbose_name='ssh私钥') # ssh key max length 4096 bit
public_key = models.CharField(max_length=1000, blank=True, verbose_name='公钥') public_key = models.CharField(max_length=1000, blank=True, verbose_name='公钥')
comment = models.TextField(max_length=200, blank=True, verbose_name='描述') comment = models.TextField(max_length=200, blank=True, verbose_name='描述')
date_expired = models.DateTimeField(default=date_expired_default, verbose_name='有效期')
created_by = models.CharField(max_length=30, default='') created_by = models.CharField(max_length=30, default='')
date_expired = models.DateTimeField(default=timezone.now()+timezone.timedelta(days=365*70), verbose_name='有效期')
class Meta: class Meta:
db_table = 'user' db_table = 'user'
def is_expired(self):
if self.date_expired > timezone.now():
return False
else:
return True
@classmethod
def init(cls):
user = cls(username='admin',
email='admin@jumpserver.org',
name='Administrator',
password=make_password('admin'),
role=Role.objects.get(name='Administrator'),
comment='Administrator is the super user of system',
created_by='System')
user.save()
user.groups.add(UserGroup.objects.get(name='ALL'))
@classmethod @classmethod
def generate_fake(cls, count=100): def generate_fake(cls, count=100):
from random import seed, randint, choice from random import seed, choice
import forgery_py import forgery_py
from django.contrib.auth.hashers import make_password
from django.db import IntegrityError from django.db import IntegrityError
seed() seed()
...@@ -127,5 +148,13 @@ class User(AbstractUser): ...@@ -127,5 +148,13 @@ class User(AbstractUser):
user.save() user.save()
def init_all_models():
for model in (Role, UserGroup, User):
if hasattr(model, 'init'):
model.init()
def generate_fake():
for model in (Role, UserGroup, User):
if hasattr(model, 'generate_fake'):
model.generate_fake()
...@@ -42,9 +42,9 @@ ...@@ -42,9 +42,9 @@
{{ form.role|bootstrap_horizontal }} {{ form.role|bootstrap_horizontal }}
{{ form.date_expired|bootstrap_horizontal }} {{ form.date_expired|bootstrap_horizontal }}
<div class="form-group"> <div class="form-group">
<label for="{{ form.enable_2FA.id_for_label }}" class="col-sm-2 control-label">二次验证</label> <label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">二次验证</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{ form.enable_2FA }} {{ form.enable_otp }}
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
<td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td> <td class="text-center" title="{% for user_group in user.group.all %} {{ user_group.name }} {% endfor %}"> {{ user.groups.all|join_queryset_attr:"name" }} </td>
<th class="text-center">{{ user.name }}</th> <th class="text-center">{{ user.name }}</th>
<td class="text-center"> <td class="text-center">
{% if user.date_expired|is_expired %} {% if user.is_expired %}
<i class="fa fa-times text-danger"></i> <i class="fa fa-times text-danger"></i>
{% else %} {% else %}
<i class="fa fa-check text-navy"></i> <i class="fa fa-check text-navy"></i>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment