Merge branch 'graph' into main
This commit is contained in:
commit
8a203161ed
|
@ -1,13 +1,20 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load key_value %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-9">
|
<div class="col-sm-12 col-md-9">
|
||||||
<div>
|
<div>
|
||||||
<h4>Challenges Solved by {{ user.username }}</h4>
|
<h4>Challenges Solved by {{ user.username }}</h4>
|
||||||
{% if solves%}
|
{% if solves%}
|
||||||
<table class="table table-dark">
|
|
||||||
<thead>
|
<div class="table table-dark">
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="time-chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="table table-dark">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">{% trans "Challenge Name" %}</th>
|
<th scope="col">{% trans "Challenge Name" %}</th>
|
||||||
<th scope="col">{% trans "Category" %}</th>
|
<th scope="col">{% trans "Category" %}</th>
|
||||||
|
@ -46,5 +53,271 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.highcharts.com/highcharts.src.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Highcharts.theme = {
|
||||||
|
colors: ['#2b908f', '#90ee7e', '#f45b5b', '#7798BF', '#aaeeee', '#ff0066',
|
||||||
|
'#eeaaee', '#55BF3B', '#DF5353', '#7798BF', '#aaeeee'],
|
||||||
|
chart: {
|
||||||
|
backgroundColor: {
|
||||||
|
linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 },
|
||||||
|
stops: [
|
||||||
|
[0, '#1D1D1D'],
|
||||||
|
[1, '#1D1D1D']
|
||||||
|
]
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontFamily: '\'Unica One\', sans-serif'
|
||||||
|
},
|
||||||
|
plotBorderColor: '#606063'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
color: '#E0E0E3',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontSize: '20px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
style: {
|
||||||
|
color: '#E0E0E3',
|
||||||
|
textTransform: 'uppercase'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
gridLineColor: '#707073',
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#E0E0E3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineColor: '#707073',
|
||||||
|
minorGridLineColor: '#505053',
|
||||||
|
tickColor: '#707073',
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
color: '#A0A0A3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
gridLineColor: '#707073',
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#E0E0E3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineColor: '#707073',
|
||||||
|
minorGridLineColor: '#505053',
|
||||||
|
tickColor: '#707073',
|
||||||
|
tickWidth: 1,
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
color: '#A0A0A3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
||||||
|
style: {
|
||||||
|
color: '#F0F0F0'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
series: {
|
||||||
|
dataLabels: {
|
||||||
|
color: '#F0F0F3',
|
||||||
|
style: {
|
||||||
|
fontSize: '13px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
lineColor: '#333'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
boxplot: {
|
||||||
|
fillColor: '#505053'
|
||||||
|
},
|
||||||
|
candlestick: {
|
||||||
|
lineColor: 'white'
|
||||||
|
},
|
||||||
|
errorbar: {
|
||||||
|
color: 'white'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
backgroundColor: '#1D1D1D',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#E0E0E3'
|
||||||
|
},
|
||||||
|
itemHoverStyle: {
|
||||||
|
color: '#FFF'
|
||||||
|
},
|
||||||
|
itemHiddenStyle: {
|
||||||
|
color: '#606063'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
style: {
|
||||||
|
color: '#C0C0C0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
style: {
|
||||||
|
color: '#666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
color: '#707073'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
drilldown: {
|
||||||
|
activeAxisLabelStyle: {
|
||||||
|
color: '#F0F0F3'
|
||||||
|
},
|
||||||
|
activeDataLabelStyle: {
|
||||||
|
color: '#F0F0F3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navigation: {
|
||||||
|
buttonOptions: {
|
||||||
|
symbolStroke: '#DDDDDD',
|
||||||
|
theme: {
|
||||||
|
fill: '#505053'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// scroll charts
|
||||||
|
rangeSelector: {
|
||||||
|
buttonTheme: {
|
||||||
|
fill: '#505053',
|
||||||
|
stroke: '#000000',
|
||||||
|
style: {
|
||||||
|
color: '#CCC'
|
||||||
|
},
|
||||||
|
states: {
|
||||||
|
hover: {
|
||||||
|
fill: '#707073',
|
||||||
|
stroke: '#000000',
|
||||||
|
style: {
|
||||||
|
color: 'white'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
fill: '#000003',
|
||||||
|
stroke: '#000000',
|
||||||
|
style: {
|
||||||
|
color: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputBoxBorderColor: '#505053',
|
||||||
|
inputStyle: {
|
||||||
|
backgroundColor: '#333',
|
||||||
|
color: 'silver'
|
||||||
|
},
|
||||||
|
labelStyle: {
|
||||||
|
color: 'silver'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navigator: {
|
||||||
|
handles: {
|
||||||
|
backgroundColor: '#666',
|
||||||
|
borderColor: '#AAA'
|
||||||
|
},
|
||||||
|
outlineColor: '#CCC',
|
||||||
|
maskFill: 'rgba(255,255,255,0.1)',
|
||||||
|
series: {
|
||||||
|
color: '#7798BF',
|
||||||
|
lineColor: '#A6C7ED'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
gridLineColor: '#505053'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollbar: {
|
||||||
|
barBackgroundColor: '#808083',
|
||||||
|
barBorderColor: '#808083',
|
||||||
|
buttonArrowColor: '#CCC',
|
||||||
|
buttonBackgroundColor: '#606063',
|
||||||
|
buttonBorderColor: '#606063',
|
||||||
|
rifleColor: '#FFF',
|
||||||
|
trackBackgroundColor: '#404043',
|
||||||
|
trackBorderColor: '#404043'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Apply the theme
|
||||||
|
Highcharts.setOptions(Highcharts.theme);
|
||||||
|
|
||||||
|
Highcharts.chart('time-chart', {
|
||||||
|
title: {
|
||||||
|
text: 'Points earned for each category'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'Points earned'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
// Use the date format in the
|
||||||
|
// labels property of the chart
|
||||||
|
labels: {
|
||||||
|
formatter: function() {
|
||||||
|
return Highcharts.dateFormat('%d.%b %Y',
|
||||||
|
this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
layout: 'vertical',
|
||||||
|
align: 'right',
|
||||||
|
verticalAlign: 'middle'
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
pointStart: {{ user.date_joined|timestamp_fromdate }},
|
||||||
|
series: {
|
||||||
|
label: {
|
||||||
|
connectorAllowed: false
|
||||||
|
},
|
||||||
|
allowPointSelect: true,
|
||||||
|
marker: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Total',
|
||||||
|
data: {{ solved|safe }}
|
||||||
|
},
|
||||||
|
{% for cat in cats %}
|
||||||
|
{
|
||||||
|
name: '{{ cat.name }}',
|
||||||
|
data: {{ pointDatas|keyvalue:cat.name|safe }},
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
{% endfor %}
|
||||||
|
],
|
||||||
|
responsive: {
|
||||||
|
rules: [{
|
||||||
|
condition: {
|
||||||
|
maxWidth: 500
|
||||||
|
},
|
||||||
|
chartOptions: {
|
||||||
|
legend: {
|
||||||
|
layout: 'horizontal',
|
||||||
|
align: 'center',
|
||||||
|
verticalAlign: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def keyvalue(dict, key):
|
||||||
|
return dict[key]
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def timestamp_fromdate(date):
|
||||||
|
return str(date.timestamp() * 1000).replace(',','.')
|
|
@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from ctfs.models import CTF_flags
|
from ctfs.models import Category, CTF_flags
|
||||||
from ..forms import UserForm,UserProfileInfoForm, UserInfosUpdateForm, UserUpdateForm
|
from ..forms import UserForm,UserProfileInfoForm, UserInfosUpdateForm, UserUpdateForm
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
@ -18,113 +18,139 @@ from accounts.models import UserProfileInfo
|
||||||
from . import connection
|
from . import connection
|
||||||
|
|
||||||
def signin(request):
|
def signin(request):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.POST.get('username')
|
username = request.POST.get('username')
|
||||||
password = request.POST.get('password')
|
password = request.POST.get('password')
|
||||||
user = authenticate(username=username, password=password)
|
user = authenticate(username=username, password=password)
|
||||||
if user:
|
if user:
|
||||||
if user.is_active:
|
if user.is_active:
|
||||||
login(request,user)
|
login(request,user)
|
||||||
return HttpResponseRedirect(reverse('home'))
|
return HttpResponseRedirect(reverse('home'))
|
||||||
else:
|
else:
|
||||||
return HttpResponse(_("Your account was inactive."))
|
return HttpResponse(_("Your account was inactive."))
|
||||||
else:
|
else:
|
||||||
return render(request, 'accounts/login.html', {'error': True})
|
return render(request, 'accounts/login.html', {'error': True})
|
||||||
else:
|
else:
|
||||||
return render(request, 'accounts/login.html', {})
|
return render(request, 'accounts/login.html', {})
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect(reverse('home'))
|
return HttpResponseRedirect(reverse('home'))
|
||||||
|
|
||||||
def signup(request):
|
def signup(request):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
user_form = UserForm()
|
user_form = UserForm()
|
||||||
profile_form = UserProfileInfoForm()
|
profile_form = UserProfileInfoForm()
|
||||||
registered = False
|
registered = False
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
pass1 = request.POST.get('password')
|
pass1 = request.POST.get('password')
|
||||||
if len(pass1) < 8:
|
if len(pass1) < 8:
|
||||||
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8})
|
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8})
|
||||||
first_isalpha = pass1[0].isalpha()
|
first_isalpha = pass1[0].isalpha()
|
||||||
if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1):
|
if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1):
|
||||||
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")})
|
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")})
|
||||||
if User.objects.filter(email=request.POST.get('email')).exists():
|
if User.objects.filter(email=request.POST.get('email')).exists():
|
||||||
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")})
|
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")})
|
||||||
user_form = UserForm(data=request.POST)
|
user_form = UserForm(data=request.POST)
|
||||||
profile_form = UserProfileInfoForm(data=request.POST)
|
profile_form = UserProfileInfoForm(data=request.POST)
|
||||||
if user_form.is_valid() and profile_form.is_valid():
|
if user_form.is_valid() and profile_form.is_valid():
|
||||||
user = user_form.save()
|
user = user_form.save()
|
||||||
user.set_password(user.password)
|
user.set_password(user.password)
|
||||||
user.save()
|
user.save()
|
||||||
profile = profile_form.save(commit=False)
|
profile = profile_form.save(commit=False)
|
||||||
profile.user = user
|
profile.user = user
|
||||||
profile.token = token_hex(16)
|
profile.token = token_hex(16)
|
||||||
profile.save()
|
profile.save()
|
||||||
registered = True
|
registered = True
|
||||||
else:
|
else:
|
||||||
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")})
|
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")})
|
||||||
return render(request,'accounts/register.html',
|
return render(request,'accounts/register.html',
|
||||||
{'user_form':user_form,
|
{'user_form':user_form,
|
||||||
'profile_form':profile_form,
|
'profile_form':profile_form,
|
||||||
'registered':registered})
|
'registered':registered})
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect(reverse('home'))
|
return HttpResponseRedirect(reverse('home'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def out(request):
|
def out(request):
|
||||||
logout(request)
|
logout(request)
|
||||||
return HttpResponseRedirect(reverse('home'))
|
return HttpResponseRedirect(reverse('home'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def edit(request):
|
def edit(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
umail = request.user.email
|
umail = request.user.email
|
||||||
uuser = request.user.username
|
uuser = request.user.username
|
||||||
p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo)
|
p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo)
|
||||||
u_form = UserUpdateForm(request.POST, instance=request.user)
|
u_form = UserUpdateForm(request.POST, instance=request.user)
|
||||||
error = None
|
error = None
|
||||||
success = None
|
success = None
|
||||||
if p_form.is_valid() and u_form.is_valid():
|
if p_form.is_valid() and u_form.is_valid():
|
||||||
pmail = u_form.cleaned_data['email']
|
pmail = u_form.cleaned_data['email']
|
||||||
if pmail == umail:
|
if pmail == umail:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if User.objects.filter(email=pmail).exists():
|
if User.objects.filter(email=pmail).exists():
|
||||||
error = _("Email already taken.")
|
error = _("Email already taken.")
|
||||||
puser = u_form.cleaned_data['username']
|
puser = u_form.cleaned_data['username']
|
||||||
if puser == uuser:
|
if puser == uuser:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if User.objects.filter(username=puser).exists():
|
if User.objects.filter(username=puser).exists():
|
||||||
error = _("Username already taken.")
|
error = _("Username already taken.")
|
||||||
if error is None:
|
if error is None:
|
||||||
u_form.save()
|
u_form.save()
|
||||||
p_form.save()
|
p_form.save()
|
||||||
success = _("Updated.")
|
success = _("Updated.")
|
||||||
request.user.username = uuser
|
request.user.username = uuser
|
||||||
|
|
||||||
context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success}
|
context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success}
|
||||||
return render(request, 'accounts/edit.html', context)
|
return render(request, 'accounts/edit.html', context)
|
||||||
else:
|
else:
|
||||||
p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo)
|
p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo)
|
||||||
u_form = UserUpdateForm(instance=request.user)
|
u_form = UserUpdateForm(instance=request.user)
|
||||||
context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token}
|
context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token}
|
||||||
return render(request, 'accounts/edit.html',context )
|
return render(request, 'accounts/edit.html',context )
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def profile(request, user_name):
|
def profile(request, user_name):
|
||||||
user_obj = get_object_or_404(User, username=user_name)
|
globalLabels= []
|
||||||
solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date')
|
globalDatas = []
|
||||||
return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves})
|
|
||||||
|
user_obj = get_object_or_404(User, username=user_name)
|
||||||
|
cats = Category.objects.all()
|
||||||
|
pointDatas = {}
|
||||||
|
|
||||||
|
for cat in cats:
|
||||||
|
# prepare categories
|
||||||
|
globalLabels.append(cat.name)
|
||||||
|
solved_count = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name).order_by('-flag_date').count()
|
||||||
|
globalDatas.append(solved_count)
|
||||||
|
# get datas
|
||||||
|
somme = 0
|
||||||
|
solved = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name).order_by('flag_date')
|
||||||
|
pointDatas[cat.name] = []
|
||||||
|
pointDatas[cat.name].append([user_obj.date_joined.timestamp() * 1000, 0])
|
||||||
|
for flag in solved:
|
||||||
|
somme += flag.ctf.points
|
||||||
|
pointDatas[cat.name].append([flag.flag_date.timestamp() * 1000, somme])
|
||||||
|
|
||||||
|
solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date')
|
||||||
|
solved = []
|
||||||
|
somme = 0
|
||||||
|
solved.append([user_obj.date_joined.timestamp() * 1000, 0])
|
||||||
|
for s in solves.reverse():
|
||||||
|
somme += s.ctf.points
|
||||||
|
solved.append([s.flag_date.timestamp() * 1000,somme])
|
||||||
|
return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'solved':solved,'globalLabels': globalLabels, 'globalDatas': globalDatas, 'pointDatas': pointDatas})
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
def rank(request, token):
|
def rank(request, token):
|
||||||
all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username')
|
all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username')
|
||||||
|
|
||||||
rank = 1
|
rank = 1
|
||||||
for elem in all_users:
|
for elem in all_users:
|
||||||
if elem.token == token:
|
if elem.token == token:
|
||||||
break
|
break
|
||||||
rank += 1
|
rank += 1
|
||||||
data = {"rank": rank}
|
data = {"rank": rank}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
|
@ -17,7 +17,7 @@ pre {background-color: #000; color: #cecece; padding-left: 15px; font-weight: bo
|
||||||
.dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF}
|
.dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF}
|
||||||
|
|
||||||
.flag_link {margin-right: 6px}
|
.flag_link {margin-right: 6px}
|
||||||
.flag_img {margin-top: 10px;width: 28px;border-radius: 100%;}
|
.flag_img {margin-top: 10px;width: 28px;}
|
||||||
|
|
||||||
.table-dark {background-color: #1D1D1D}
|
.table-dark {background-color: #1D1D1D}
|
||||||
.table-dark td, .table-dark th, .table-dark thead th {border: none}
|
.table-dark td, .table-dark th, .table-dark thead th {border: none}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pre {background-color: #000; color: #cecece; padding-left: 15px; font-weight: bo
|
||||||
.dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF}
|
.dropdown-item:hover {background-color: #1D1D1D; color: #FFFFFF}
|
||||||
|
|
||||||
.flag_link {margin-right: 6px}
|
.flag_link {margin-right: 6px}
|
||||||
.flag_img {margin-top: 10px;width: 28px;border-radius: 100%;}
|
.flag_img {margin-top: 10px;width: 28px;}
|
||||||
|
|
||||||
.table-dark {background-color: #1D1D1D}
|
.table-dark {background-color: #1D1D1D}
|
||||||
.table-dark td, .table-dark th, .table-dark thead th {border: none}
|
.table-dark td, .table-dark th, .table-dark thead th {border: none}
|
||||||
|
|
Loading…
Reference in New Issue