API SSO, Beautification of Tables, and more. (#562)

# One Thousandth Commit 🎉 🎈 🎆 🍾

* Allow requiring API ownership validation by SSO.
Closes #163

* Add Discourse group name length restrictions.

* Redirect after api addition/deletion of main character

* Correct admin searching for removed discourse_username field in AuthServicesInfo

* Correct admin function to sync user Discourse groups

* Beautify tables by removing borders and hiding when empty.

*Add buttons on dead-end pages to return to originating view.
This commit is contained in:
Adarnof 2016-10-27 23:28:00 -04:00 committed by GitHub
parent 4ea7fdeaf2
commit e77c162fa0
26 changed files with 313 additions and 135 deletions

View File

@ -344,6 +344,8 @@ ALLIANCE_NAME = os.environ.get('AA_ALLIANCE_NAME', '')
# BLUE_API_ACCOUNT - Require API to be for Account and not character restricted # BLUE_API_ACCOUNT - Require API to be for Account and not character restricted
# REJECT_OLD_APIS - Require each submitted API be newer than the latest submitted API # REJECT_OLD_APIS - Require each submitted API be newer than the latest submitted API
# REJECT_OLD_APIS_MARGIN - Margin from latest submitted API ID within which a newly submitted API is still accepted # REJECT_OLD_APIS_MARGIN - Margin from latest submitted API ID within which a newly submitted API is still accepted
# API_SSO_VALIDATION - Require users to prove ownership of newly entered API keys via SSO
# Requires SSO to be configured.
####################### #######################
MEMBER_API_MASK = os.environ.get('AA_MEMBER_API_MASK', 268435455) MEMBER_API_MASK = os.environ.get('AA_MEMBER_API_MASK', 268435455)
MEMBER_API_ACCOUNT = 'True' == os.environ.get('AA_MEMBER_API_ACCOUNT', 'True') MEMBER_API_ACCOUNT = 'True' == os.environ.get('AA_MEMBER_API_ACCOUNT', 'True')
@ -351,6 +353,7 @@ BLUE_API_MASK = os.environ.get('AA_BLUE_API_MASK', 8388608)
BLUE_API_ACCOUNT = 'True' == os.environ.get('AA_BLUE_API_ACCOUNT', 'False') BLUE_API_ACCOUNT = 'True' == os.environ.get('AA_BLUE_API_ACCOUNT', 'False')
REJECT_OLD_APIS = 'True' == os.environ.get('AA_REJECT_OLD_APIS', 'False') REJECT_OLD_APIS = 'True' == os.environ.get('AA_REJECT_OLD_APIS', 'False')
REJECT_OLD_APIS_MARGIN = os.environ.get('AA_REJECT_OLD_APIS_MARGIN', 50) REJECT_OLD_APIS_MARGIN = os.environ.get('AA_REJECT_OLD_APIS_MARGIN', 50)
API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False')
##################### #####################
# Alliance Market # Alliance Market

View File

@ -39,6 +39,7 @@ urlpatterns = [
# Eve Online # Eve Online
url(r'^main_character_change/(\w+)/$', eveonline.views.main_character_change, url(r'^main_character_change/(\w+)/$', eveonline.views.main_character_change,
name='auth_main_character_change'), name='auth_main_character_change'),
url(r'^api_verify_owner/(\w+)/$', eveonline.views.api_sso_validate, name='auth_api_sso'),
# Forum Service Control # Forum Service Control
url(r'^activate_forum/$', services.views.activate_forum, name='auth_activate_forum'), url(r'^activate_forum/$', services.views.activate_forum, name='auth_activate_forum'),

View File

@ -99,7 +99,7 @@ class AuthServicesInfoManager(admin.ModelAdmin):
def sync_discourse(self, request, queryset): def sync_discourse(self, request, queryset):
count = 0 count = 0
for a in queryset: for a in queryset:
if a.discourse_username != "": if a.discourse_enabled:
update_discourse_groups.delay(a.user.pk) update_discourse_groups.delay(a.user.pk)
count += 1 count += 1
self.message_user(request, "%s discourse accounts queued for group sync." % count) self.message_user(request, "%s discourse accounts queued for group sync." % count)
@ -137,7 +137,6 @@ class AuthServicesInfoManager(admin.ModelAdmin):
'mumble_username', 'mumble_username',
'teamspeak3_uid', 'teamspeak3_uid',
'discord_uid', 'discord_uid',
'discourse_username',
'ips4_username', 'ips4_username',
'smf_username', 'smf_username',
'market_username', 'market_username',

View File

@ -4,6 +4,7 @@ from django.conf import settings
from services.managers.eve_api_manager import EveApiManager from services.managers.eve_api_manager import EveApiManager
from eveonline.managers import EveManager from eveonline.managers import EveManager
from eveonline.models import EveApiKeyPair
import evelink import evelink
import logging import logging
@ -31,7 +32,9 @@ class UpdateKeyForm(forms.Form):
if EveManager.check_if_api_key_pair_exist(self.cleaned_data['api_id']): if EveManager.check_if_api_key_pair_exist(self.cleaned_data['api_id']):
logger.debug("UpdateKeyForm failed cleaning as API id %s already exists." % self.cleaned_data['api_id']) logger.debug("UpdateKeyForm failed cleaning as API id %s already exists." % self.cleaned_data['api_id'])
raise forms.ValidationError('API key already exist') if EveApiKeyPair.objects.get(api_id=self.cleaned_data['api_id']).user:
# allow orphaned APIs to proceed to SSO validation upon re-entry
raise forms.ValidationError('API key already exist')
if settings.REJECT_OLD_APIS and not EveManager.check_if_api_key_pair_is_new( if settings.REJECT_OLD_APIS and not EveManager.check_if_api_key_pair_is_new(
self.cleaned_data['api_id'], self.cleaned_data['api_id'],
settings.REJECT_OLD_APIS_MARGIN): settings.REJECT_OLD_APIS_MARGIN):

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-26 01:49
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0002_remove_eveapikeypair_error_count'),
]
operations = [
migrations.AlterField(
model_name='eveapikeypair',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='evecharacter',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -14,7 +14,7 @@ class EveCharacter(models.Model):
alliance_id = models.CharField(max_length=254) alliance_id = models.CharField(max_length=254)
alliance_name = models.CharField(max_length=254) alliance_name = models.CharField(max_length=254)
api_id = models.CharField(max_length=254) api_id = models.CharField(max_length=254)
user = models.ForeignKey(User) user = models.ForeignKey(User, blank=True, null=True)
def __str__(self): def __str__(self):
return self.character_name return self.character_name
@ -24,10 +24,10 @@ class EveCharacter(models.Model):
class EveApiKeyPair(models.Model): class EveApiKeyPair(models.Model):
api_id = models.CharField(max_length=254) api_id = models.CharField(max_length=254)
api_key = models.CharField(max_length=254) api_key = models.CharField(max_length=254)
user = models.ForeignKey(User) user = models.ForeignKey(User, blank=True, null=True)
def __str__(self): def __str__(self):
return self.user.username + " - ApiKeyPair" return self.api_id
@python_2_unicode_compatible @python_2_unicode_compatible

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.shortcuts import render, redirect from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
@ -7,11 +7,13 @@ from eveonline.forms import UpdateKeyForm
from eveonline.managers import EveManager from eveonline.managers import EveManager
from authentication.managers import AuthServicesInfoManager from authentication.managers import AuthServicesInfoManager
from services.managers.eve_api_manager import EveApiManager from services.managers.eve_api_manager import EveApiManager
from eveonline.models import EveApiKeyPair from eveonline.models import EveApiKeyPair, EveCharacter
from authentication.models import AuthServicesInfo from authentication.models import AuthServicesInfo
from authentication.tasks import set_state from authentication.tasks import set_state
from eveonline.tasks import refresh_api from eveonline.tasks import refresh_api
from eve_sso.decorators import token_required
from django.conf import settings
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -24,20 +26,39 @@ def add_api_key(request):
form = UpdateKeyForm(request.user, request.POST) form = UpdateKeyForm(request.user, request.POST)
logger.debug("Request type POST with form valid: %s" % form.is_valid()) logger.debug("Request type POST with form valid: %s" % form.is_valid())
if form.is_valid(): if form.is_valid():
EveManager.create_api_keypair(form.cleaned_data['api_id'], if EveApiKeyPair.objects.filter(api_id=form.cleaned_data['api_id'],
form.cleaned_data['api_key'], api_key=form.cleaned_data['api_key']).exists():
request.user) # allow orphaned keys to proceed to SSO validation upon re-entry
api_key = EveApiKeyPair.objects.get(api_id=form.cleaned_data['api_id'],
api_key=form.cleaned_data['api_key'])
elif EveApiKeyPair.objects.filter(api_id=form.cleaned_data['api_id']).exists():
logger.warn('API %s re-added with different vcode.' % form.cleaned_data['api_id'])
EveApiKeyPair.objects.filter(api_id=form.cleaned_data['api_id']).delete()
api_key = EveApiKeyPair.objects.create(api_id=form.cleaned_data['api_id'],
api_key=form.cleaned_data['api_key'])
else:
api_key = EveApiKeyPair.objects.create(api_id=form.cleaned_data['api_id'],
api_key=form.cleaned_data['api_key'])
owner = None
if not settings.API_SSO_VALIDATION:
# set API and character owners if SSO validation not requested
api_key.user = request.user
api_key.save()
owner = request.user
# Grab characters associated with the key pair # Grab characters associated with the key pair
characters = EveApiManager.get_characters_from_api(form.cleaned_data['api_id'], characters = EveApiManager.get_characters_from_api(form.cleaned_data['api_id'],
form.cleaned_data['api_key']) form.cleaned_data['api_key'])
EveManager.create_characters_from_list(characters, request.user, form.cleaned_data['api_id']) EveManager.create_characters_from_list(characters, owner, form.cleaned_data['api_id'])
logger.info("Successfully processed api add form for user %s" % request.user) logger.info("Successfully processed api add form for user %s" % request.user)
messages.success(request, 'Added API key %s to your account.' % form.cleaned_data['api_id']) if not settings.API_SSO_VALIDATION:
auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0] messages.success(request, 'Added API key %s to your account.' % form.cleaned_data['api_id'])
if not auth.main_char_id: auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
messages.warning(request, 'Please select a main character.') if not auth.main_char_id:
return redirect("/api_key_management/") return redirect('auth_characters')
return redirect("/api_key_management/")
else:
logger.debug('Requesting SSO validation of API %s by user %s' % (api_key.api_id, request.user))
return render(request, 'registered/apisso.html', context={'api':api_key})
else: else:
logger.debug("Form invalid: returning to form.") logger.debug("Form invalid: returning to form.")
else: else:
@ -47,6 +68,32 @@ def add_api_key(request):
return render(request, 'registered/addapikey.html', context=context) return render(request, 'registered/addapikey.html', context=context)
@login_required
@token_required(new=True)
def api_sso_validate(request, tokens, api_id):
logger.debug('api_sso_validate called by user %s for api %s' % (request.user, api_id))
api = get_object_or_404(EveApiKeyPair, api_id=api_id)
if api.user:
logger.warning('User %s attempting to take ownership of api %s from %s' % (request.user, api_id, api.user))
messages.warning(request, 'API %s already claimed by user %s' % (api_id, api.user))
return redirect('auth_api_key_management')
token = tokens[0]
logger.debug('API %s has no owner. Checking if token for %s matches.' % (api_id, token.character_name))
characters = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
if token.character_id in characters:
api.user = request.user
api.save()
EveCharacter.objects.filter(character_id__in=characters).update(user=request.user, api_id=api_id)
messages.success(request, 'Confirmed ownership of API %s' % api.api_id)
auth, c = AuthServicesInfo.objects.get_or_create(user=request.user)
if not auth.main_char_id:
return redirect('auth_characters')
return redirect('auth_api_key_management')
else:
messages.warning(request, '%s not found on API %s. Please SSO as a character on the API.' % (token.character_name, api.api_id))
return render(request, 'registered/apisso.html', context={'api':api})
@login_required @login_required
def api_key_management_view(request): def api_key_management_view(request):
logger.debug("api_key_management_view called by user %s" % request.user) logger.debug("api_key_management_view called by user %s" % request.user)
@ -59,21 +106,17 @@ def api_key_management_view(request):
def api_key_removal(request, api_id): def api_key_removal(request, api_id):
logger.debug("api_key_removal called by user %s for api id %s" % (request.user, api_id)) logger.debug("api_key_removal called by user %s for api id %s" % (request.user, api_id))
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0] authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
# Check if our users main id is in the to be deleted characters
characters = EveManager.get_characters_by_owner_id(request.user.id)
if characters is not None:
for character in characters:
if character.character_id == authinfo.main_char_id:
if character.api_id == api_id:
messages.warning(request,
'You have deleted your main character. Please select a new main character.')
set_state(request.user)
EveManager.delete_api_key_pair(api_id, request.user.id) EveManager.delete_api_key_pair(api_id, request.user.id)
EveManager.delete_characters_by_api_id(api_id, request.user.id) EveManager.delete_characters_by_api_id(api_id, request.user.id)
messages.success(request, 'Deleted API key %s' % api_id) messages.success(request, 'Deleted API key %s' % api_id)
logger.info("Succesfully processed api delete request by user %s for api %s" % (request.user, api_id)) logger.info("Succesfully processed api delete request by user %s for api %s" % (request.user, api_id))
return redirect("auth_api_key_management") if EveCharacter.objects.filter(character_id=authinfo.main_char_id).exists():
return redirect("auth_api_key_management")
else:
authinfo.main_char_id = None
authinfo.save()
set_state(request.user)
return redirect("auth_characters")
@login_required @login_required
@ -89,8 +132,8 @@ def main_character_change(request, char_id):
logger.debug("main_character_change called by user %s for character id %s" % (request.user, char_id)) logger.debug("main_character_change called by user %s for character id %s" % (request.user, char_id))
if EveManager.check_if_character_owned_by_user(char_id, request.user): if EveManager.check_if_character_owned_by_user(char_id, request.user):
AuthServicesInfoManager.update_main_char_id(char_id, request.user) AuthServicesInfoManager.update_main_char_id(char_id, request.user)
set_state(request.user)
messages.success(request, 'Changed main character ID to %s' % char_id) messages.success(request, 'Changed main character ID to %s' % char_id)
set_state(request.user)
return redirect("auth_characters") return redirect("auth_characters")
messages.error(request, 'Failed to change main character - selected character is not owned by your account.') messages.error(request, 'Failed to change main character - selected character is not owned by your account.')
return redirect("auth_characters") return redirect("auth_characters")

View File

@ -343,7 +343,10 @@ class DiscourseManager:
@staticmethod @staticmethod
def _sanitize_groupname(name): def _sanitize_groupname(name):
name = name.strip(' _') name = name.strip(' _')
return re.sub('[^\w]', '', name) name = re.sub('[^\w]', '', name)
if len(name) < 3:
name = name + "".join('_' for i in range(3-len(name)))
return name[:20]
@staticmethod @staticmethod
def update_groups(user): def update_groups(user):

View File

@ -9,38 +9,36 @@
{% block extra_css %}{% endblock extra_css %} {% block extra_css %}{% endblock extra_css %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-6 col-lg-offset-3">
<div class="row"> <div class="row">
{% if apikeypairs %}
{% else %}
<div class="alert alert-danger" role="alert">{% trans "No api keys found" %}</div>
{% endif %}
<h1 class="page-header text-center">{% trans "API Key Management" %} <h1 class="page-header text-center">{% trans "API Key Management" %}
<div class="text-right"> <div class="text-right">
<a href="{% url 'auth_add_api_key' %}" class="btn btn-success">{% trans "Add Key" %}</a> <a href="{% url 'auth_add_api_key' %}" class="btn btn-success">{% trans "Add Key" %}</a>
</div> </div>
</h1> </h1>
<table class="table table-bordered"> {% if apikeypairs %}
<tr> <table class="table">
<th class="text-center">{% trans "API ID" %}</th>
<th class="text-center">{% trans "API Key" %}</th>
<th class="text-center">{% trans "Action" %}</th>
</tr>
{% for pair in apikeypairs %}
<tr> <tr>
<td class="text-center">{{ pair.api_id }}</td> <th class="text-center">{% trans "API ID" %}</th>
<td class="text-center">{{ pair.api_key }}</td> <th class="text-center">{% trans "Action" %}</th>
<td class="text-center">
<a href="{% url 'auth_user_refresh_api' pair.api_id %}" class="btn btn-success">
<span class="glyphicon glyphicon-refresh"></span>
</a>
<a href="{% url 'auth_api_key_removal' pair.api_id %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
</td>
</tr> </tr>
{% endfor %} {% for pair in apikeypairs %}
</table> <tr>
<td class="text-center">{{ pair.api_id }}</td>
<td class="text-center">
<a href="{% url 'auth_user_refresh_api' pair.api_id %}" class="btn btn-success">
<span class="glyphicon glyphicon-refresh"></span>
</a>
<a href="{% url 'auth_api_key_removal' pair.api_id %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-danger" role="alert">{% trans "No api keys found" %}</div>
{% endif %}
</div> </div>
</div> </div>

View File

@ -0,0 +1,21 @@
{% extends 'public/base.html' %}
{% load staticfiles %}
{% block title %}Verify API Ownership{% endblock %}
{% block page_title%}Verify API Ownership{% endblock %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">Verify API Ownership</div>
<div class="col-xs-4 col-xs-offset-4">
<div class="panel panel-default>
<div class="panel-header">Please authenticate as a character on API {{ api.api_id }} to prove ownership.</div>
<div class="panel-body">
<div class="text-center">
<a href="{% url 'auth_api_sso' api.api_id %}">
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}">
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -17,7 +17,7 @@
{% endif %} {% endif %}
</h1> </h1>
<h2>{% blocktrans %}{{ user }} has collected {{ n_fats }} links this month.{% endblocktrans %}</h2> <h2>{% blocktrans %}{{ user }} has collected {{ n_fats }} links this month.{% endblocktrans %}</h2>
<table class="table table-responsive table-bordered"> <table class="table table-responsive">
<tr> <tr>
<th class="col-md-2 text-center">{% trans "Ship" %}</th> <th class="col-md-2 text-center">{% trans "Ship" %}</th>
<th class="col-md-2 text-center">{% trans "Times used" %}</th> <th class="col-md-2 text-center">{% trans "Times used" %}</th>
@ -31,7 +31,8 @@
</table> </table>
{% if created_fats %} {% if created_fats %}
<h2>{% blocktrans %}{{ user }} has created {{ n_created_fats }} links this month.{% endblocktrans %}</h2> <h2>{% blocktrans %}{{ user }} has created {{ n_created_fats }} links this month.{% endblocktrans %}</h2>
<table class="table table-bordered"> {% if created_fats %}
<table class="table">
<tr> <tr>
<th class="text-center">{% trans "Name" %}</th> <th class="text-center">{% trans "Name" %}</th>
<th class="text-center">{% trans "Creator" %}</th> <th class="text-center">{% trans "Creator" %}</th>
@ -58,6 +59,7 @@
</table> </table>
{% endif %} {% endif %}
{% endif %}
</div> </div>

View File

@ -16,7 +16,8 @@
{% endif %} {% endif %}
</div> </div>
</h1> </h1>
<table class="table table-responsive table-bordered"> <div class="col-lg-2 col-lg-offset-5">
<table class="table table-responsive">
<tr> <tr>
<th class="col-md-2 text-center">{% trans "Month" %}</th> <th class="col-md-2 text-center">{% trans "Month" %}</th>
<th class="col-md-2 text-center">{% trans "Fats" %}</th> <th class="col-md-2 text-center">{% trans "Fats" %}</th>
@ -32,6 +33,7 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
</div>
</div> </div>

View File

@ -16,7 +16,8 @@
{% endif %} {% endif %}
</div> </div>
</h1> </h1>
<table class="table table-responsive table-bordered"> {% if fatStats %}
<table class="table table-responsive">
<tr> <tr>
<th class="col-md-1"></th> <th class="col-md-1"></th>
<th class="col-md-2 text-center">{% trans "Ticker" %}</th> <th class="col-md-2 text-center">{% trans "Ticker" %}</th>
@ -38,6 +39,7 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% endif %}
</div> </div>

View File

@ -22,7 +22,8 @@
</th> </th>
</tr> </tr>
</table> </table>
<table class="table table-responsive table-bordered"> {% if fats %}
<table class="table table-responsive">
<tr> <tr>
<th class="text-center">{% trans "fatname" %}</th> <th class="text-center">{% trans "fatname" %}</th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% trans "Character" %}</th>
@ -44,6 +45,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No fleet activity on record.</div>
{% endif %}
{% if perms.auth.fleetactivitytracking%} {% if perms.auth.fleetactivitytracking%}
<table class="table"> <table class="table">
@ -64,7 +68,8 @@
</th> </th>
</tr> </tr>
</table> </table>
<table class="table table-bordered"> {% if fatlinks %}
<table class="table">
<tr> <tr>
<th class="text-center">{% trans "Name" %}</th> <th class="text-center">{% trans "Name" %}</th>
<th class="text-center">{% trans "Creator" %}</th> <th class="text-center">{% trans "Creator" %}</th>
@ -89,6 +94,9 @@
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No created fatlinks on record.</div>
{% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@ -10,59 +10,73 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h3 class="page-header text-center">{% trans "Group Management" %}</h3> <h3 class="page-header text-center">{% trans "Group Management" %}</h3>
<h4 class="page-header text-center">{% trans "Group Add Request" %}</h4> <ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#add">{% trans "Group Add Requests" %}</a></li>
<table class="table table-bordered"> <li><a data-toggle="tab" href="#leave">{% trans "Group Leave Requests" %}</a></li>
<tr> </ul>
<th class="text-center">{% trans "RequestID" %}</th> <div class="tab-content">
<th class="text-center">{% trans "CharacterName" %}</th> <div id="add" class="tab-pane fade in active">
<th class="text-center">{% trans "GroupName" %}</th> <div class="panel-body">
<th class="text-center">{% trans "Action" %}</th> {% if acceptrequests %}
</tr> <table class="table">
<tr>
{% for acceptrequest in acceptrequests %} <th class="text-center">{% trans "RequestID" %}</th>
<tr> <th class="text-center">{% trans "CharacterName" %}</th>
<td class="text-center">{{ acceptrequest.id }}</td> <th class="text-center">{% trans "GroupName" %}</th>
<td class="text-center">{{ acceptrequest.main_char.character_name }}</td> <th class="text-center">{% trans "Action" %}</th>
<td class="text-center">{{ acceptrequest.group.name }}</td> </tr>
<td class="text-center"> {% for acceptrequest in acceptrequests %}
<a href="{% url 'auth_group_accept_request' acceptrequest.id %}" class="btn btn-success"> <tr>
{% trans "Accept" %} <td class="text-center">{{ acceptrequest.id }}</td>
</a> <td class="text-center">{{ acceptrequest.main_char.character_name }}</td>
<td class="text-center">{{ acceptrequest.group.name }}</td>
<a href="{% url 'auth_group_reject_request' acceptrequest.id %}" class="btn btn-danger"> <td class="text-center">
{% trans "Reject" %} <a href="{% url 'auth_group_accept_request' acceptrequest.id %}" class="btn btn-success">
</a> {% trans "Accept" %}
</td> </a>
</tr> <a href="{% url 'auth_group_reject_request' acceptrequest.id %}" class="btn btn-danger">
{% endfor %} {% trans "Reject" %}
</table> </a>
<h4 class="page-header text-center">{% trans "Group Leave Request" %}</h4> </td>
<table class="table table-bordered"> </tr>
<tr> {% endfor %}
<th class="text-center">{% trans "RequestID" %}</th> </table>
<th class="text-center">{% trans "CharacterName" %}</th> {% else %}
<th class="text-center">{% trans "GroupName" %}</th> <div class="alert alert-warning text-center">No group add requests.</div>
<th class="text-center">{% trans "Action" %}</th> {% endif %}
</tr> </div>
</div>
{% for leaverequest in leaverequests %} <div id="leave" class="tab-pane fade">
<tr> <div class="panel-body">
<td class="text-center">{{ leaverequest.id }}</td> {% if leaverequests %}
<td class="text-center">{{ leaverequest.main_char.character_name }}</td> <table class="table">
<td class="text-center">{{ leaverequest.group.name }}</td> <tr>
<td class="text-center"> <th class="text-center">{% trans "RequestID" %}</th>
<a href="{% url 'auth_group_leave_accept_request' leaverequest.id %}" class="btn btn-success"> <th class="text-center">{% trans "CharacterName" %}</th>
{% trans "Accept" %} <th class="text-center">{% trans "GroupName" %}</th>
</a> <th class="text-center">{% trans "Action" %}</th>
</tr>
<a href="{% url 'auth_group_leave_reject_request' leaverequest.id %}" class="btn btn-danger"> {% for leaverequest in leaverequests %}
{% trans "Reject" %} <tr>
</a> <td class="text-center">{{ leaverequest.id }}</td>
</td> <td class="text-center">{{ leaverequest.main_char.character_name }}</td>
</tr> <td class="text-center">{{ leaverequest.group.name }}</td>
{% endfor %} <td class="text-center">
</table> <a href="{% url 'auth_group_leave_accept_request' leaverequest.id %}" class="btn btn-success">
{% trans "Accept" %}
</a>
<a href="{% url 'auth_group_leave_reject_request' leaverequest.id %}" class="btn btn-danger">
{% trans "Reject" %}
</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-warning text-center">No group leave requests.</div>
{% endif %}
</div>
</div>
</div>
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -11,9 +11,9 @@
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Available Groups" %}</h1> <h1 class="page-header text-center">{% trans "Available Groups" %}</h1>
{% if STATE == MEMBER_STATE or user.is_superuser %} {% if STATE == MEMBER_STATE or user.is_superuser %}
<table class="table table-bordered"> {% if pairs %}
<table class="table">
<tr> <tr>
<th class="text-center">{% trans "GroupID" %}</th>
<th class="text-center">{% trans "GroupName" %}</th> <th class="text-center">{% trans "GroupName" %}</th>
<th class="text-center">{% trans "GroupDesc" %}</th> <th class="text-center">{% trans "GroupDesc" %}</th>
<th class="text-center">{% trans "Action" %}</th> <th class="text-center">{% trans "Action" %}</th>
@ -21,7 +21,6 @@
{% for pair in pairs %} {% for pair in pairs %}
<tr> <tr>
<td class="text-center">{{ pair.0.id }}</td>
<td class="text-center">{{ pair.0.name }}</td> <td class="text-center">{{ pair.0.name }}</td>
<td class="text-center">{{ pair.1.description }}</td> <td class="text-center">{{ pair.1.description }}</td>
<td class="text-center"> <td class="text-center">
@ -48,6 +47,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No groups available.</div>
{% endif %}
{% else %} {% else %}
{% if IS_CORP %} {% if IS_CORP %}
<div class="alert alert-danger" role="alert">{% trans "You are not in the corporation." %}</div> <div class="alert alert-danger" role="alert">{% trans "You are not in the corporation." %}</div>

View File

@ -10,7 +10,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
{% if not STATE == MEMBER_STATE and not user.is_superuser %} {% if not STATE == MEMBER_STATE %}
<h1 class="page-header text-center">{% trans "Personal Applications" %} <h1 class="page-header text-center">{% trans "Personal Applications" %}
<div class="text-right"> <div class="text-right">
{% if create %} {% if create %}
@ -20,7 +20,8 @@
{% endif %} {% endif %}
</div> </div>
</h1> </h1>
<table class="table table-bordered table-condensed"> {% if personal_apps %}
<table class="table table-condensed">
<tr> <tr>
<th class="text-center">{% trans "Username" %}</th> <th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Corporation" %} <th class="text-center">{% trans "Corporation" %}
@ -54,6 +55,7 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% endif %}
{% endif %} {% endif %}
{% if perms.auth.human_resources %} {% if perms.auth.human_resources %}
<h1 class="page-header text-center">{% trans "Application Management" %} <h1 class="page-header text-center">{% trans "Application Management" %}
@ -71,6 +73,7 @@
<div class="tab-content"> <div class="tab-content">
<div id="pending" class="tab-pane fade in active"> <div id="pending" class="tab-pane fade in active">
<div class="panel-body"> <div class="panel-body">
{% if applications %}
<table class="table"> <table class="table">
<tr> <tr>
<th class="text-center">{% trans "Date" %}</th> <th class="text-center">{% trans "Date" %}</th>
@ -107,10 +110,14 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No pending applications.</div>
{% endif %}
</div> </div>
</div> </div>
<div id="reviewed" class="tab-pane fade"> <div id="reviewed" class="tab-pane fade">
<div class="panel-body"> <div class="panel-body">
{% if finished_applications %}
<table class="table"> <table class="table">
<tr> <tr>
<th class="text-center">{% trans "Date" %}</th> <th class="text-center">{% trans "Date" %}</th>
@ -147,6 +154,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No reviewed applications.</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,17 +11,17 @@
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "View Application" %}</h1> <h1 class="page-header text-center">{% trans "View Application" %}</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="col-md-4 col-md-offset-4"> <div class="col-md-6 col-md-offset-3">
<div class="row"> <div class="row">
{% if app.approved %} {% if app.approved %}
<div class="alert alert-success">{% trans "Approved" %}</div> <div class="alert alert-success text-center">{% trans "Approved" %}</div>
{% elif app.approved == False %} {% elif app.approved == False %}
<div class="alert alert-danger">{% trans "Denied" %}</div> <div class="alert alert-danger text-center">{% trans "Denied" %}</div>
{% else %} {% else %}
<div class="alert alert-warning">{% trans "Pending" %}</div> <div class="alert alert-warning text-center">{% trans "Pending" %}</div>
{% endif %} {% endif %}
{% if app.reviewer_str %} {% if app.reviewer_str %}
<div class="alert alert-info">{% trans "Reviewer:" %} {{ app.reviewer_str }}</div> <div class="alert alert-info text-center">{% trans "Reviewer:" %} {{ app.reviewer_str }}</div>
{% endif %} {% endif %}
</div> </div>
<div class="row"> <div class="row">

View File

@ -22,6 +22,7 @@
<div id="unread" class="tab-pane fade in active"> <div id="unread" class="tab-pane fade in active">
<div class="panel-body"> <div class="panel-body">
<div class="table-responsive"> <div class="table-responsive">
{% if unread %}
<table class="table table-condensed table-hover table-striped"> <table class="table table-condensed table-hover table-striped">
<tr> <tr>
<th class="text-center">{% trans "Timestamp" %}</th> <th class="text-center">{% trans "Timestamp" %}</th>
@ -43,12 +44,16 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No unread notifications.</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
<div id="read" class="tab-pane fade"> <div id="read" class="tab-pane fade">
<div class="panel-body"> <div class="panel-body">
<div class="table-responsive"> <div class="table-responsive">
{% if read %}
<table class="table table-condensed table-hover table-striped"> <table class="table table-condensed table-hover table-striped">
<tr> <tr>
<th class="text-center">{% trans "Timestamp" %}</th> <th class="text-center">{% trans "Timestamp" %}</th>
@ -70,6 +75,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No read notifications.</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,7 +8,14 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "View Notification" %}</h1> <h1 class="page-header text-center">
{% trans "View Notification" %}
<div class="text-right">
<a href="{% url 'auth_notification_list' %}" class="btn btn-primary btn-lg">
<span class="glyphicon glyphicon-arrow-left"></span>
</a>
</div>
</h1>
<div class="col-lg-12 container" id="example"> <div class="col-lg-12 container" id="example">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">

View File

@ -17,13 +17,14 @@
{% endif %} {% endif %}
</div> </div>
</h1> </h1>
<div class="col-lg-12 text-center"> <div class="col-lg-12 text-center row">
<div class="label label-info text-left"> <div class="label label-info text-left">
<b>{% trans "Current Eve Time:" %} </b> <b>{% trans "Current Eve Time:" %} </b>
</div><div class="label label-info text-left" id="current-time"></div> </div><div class="label label-info text-left" id="current-time"></div>
<br/> <br />
</div> </div>
<table class="table table-responsive table-bordered"> {% if optimer %}
<table class="table table-responsive">
<tr> <tr>
<th class="text-center">{% trans "Operation Name" %}</th> <th class="text-center">{% trans "Operation Name" %}</th>
<th class="text-center">{% trans "Doctrine" %}</th> <th class="text-center">{% trans "Doctrine" %}</th>
@ -70,7 +71,9 @@
{% endfor %} {% endfor %}
</tr> </tr>
</table> </table>
{% else %}
<br /><div class="alert alert-warning text-center">No fleet operations found.</div>
{% endif %}
</div> </div>
<script src="/static/js/dateformat.js"></script> <script src="/static/js/dateformat.js"></script>

View File

@ -28,6 +28,9 @@
<div class="alert alert-info" role="alert">{% blocktrans %}Give this link to the line members{% endblocktrans %}.</div> <div class="alert alert-info" role="alert">{% blocktrans %}Give this link to the line members{% endblocktrans %}.</div>
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
http://{{ request.get_host }}{% url 'auth_srp_request_view' completed_srp_code %}</div> http://{{ request.get_host }}{% url 'auth_srp_request_view' completed_srp_code %}</div>
<div class="text-center">
<a href="{% url 'auth_srp_management_view' %}" class="btn btn-primary btn-lg">{% trans "Continue" %}</a>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -34,7 +34,8 @@
<b>{% trans "Total ISK Cost:" %} {{ totalcost | intcomma }}</b> <b>{% trans "Total ISK Cost:" %} {{ totalcost | intcomma }}</b>
</div> </div>
</div> </div>
<table class="table table-bordered"> {% if srpfleetrequests %}
<table class="table">
<tr> <tr>
<th class="text-center">{% trans "Pilot Name" %}</th> <th class="text-center">{% trans "Pilot Name" %}</th>
<th class="text-center">{% trans "Killboard Link" %}</th> <th class="text-center">{% trans "Killboard Link" %}</th>
@ -120,6 +121,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No SRP requests for this fleet.</div>
{% endif %}
</div> </div>
</div> </div>

View File

@ -30,6 +30,9 @@
</form> </form>
{% else %} {% else %}
<div class="alert alert-success" role="alert">{% trans "SRP Request Successfully Submitted" %}</div> <div class="alert alert-success" role="alert">{% trans "SRP Request Successfully Submitted" %}</div>
<div class="text-center">
<a href="{% url 'auth_srp_management_view' %}" class="btn btn-primary btn-lg">{% trans 'Continue' %}</a>
</div>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@ -29,7 +29,8 @@
<b>{% trans "Total ISK Cost:" %} {{ totalcost | intcomma }}</b> <b>{% trans "Total ISK Cost:" %} {{ totalcost | intcomma }}</b>
</div> </div>
</div> </div>
<table class="table table-bordered"> {% if srpfleets %}
<table class="table">
<tr> <tr>
<th class="text-center">{% trans "Fleet Name" %}</th> <th class="text-center">{% trans "Fleet Name" %}</th>
<th class="text-center">{% trans "Fleet Time" %}</th> <th class="text-center">{% trans "Fleet Time" %}</th>
@ -117,6 +118,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No SRP fleets created.</div>
{% endif %}
</div> </div>
</div> </div>

View File

@ -24,7 +24,7 @@
</div> </div>
{% if corp_timers %} {% if corp_timers %}
<h4><b>{% trans "Corp Timers" %}</b></h4> <h4><b>{% trans "Corp Timers" %}</b></h4>
<table class="table table-bordered"> <table class="table">
<tr> <tr>
<th style="width:150px" class="text-center">{% trans "Details" %}</th> <th style="width:150px" class="text-center">{% trans "Details" %}</th>
<th class="text-center">{% trans "Objective" %}</th> <th class="text-center">{% trans "Objective" %}</th>
@ -124,7 +124,8 @@
</table> </table>
{% endif %} {% endif %}
<h4><b>{% trans "Next Timers" %}</b></h4> <h4><b>{% trans "Next Timers" %}</b></h4>
<table class="table table-bordered"> {% if future_timers %}
<table class="table">
<tr> <tr>
<th style="width:150px" class="text-center">{% trans "Details" %}</th> <th style="width:150px" class="text-center">{% trans "Details" %}</th>
<th class="text-center">{% trans "Objective" %}</th> <th class="text-center">{% trans "Objective" %}</th>
@ -221,8 +222,12 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No upcoming timers.</div>
{% endif %}
<h4><b>{% trans "Past Timers" %}</b></h4> <h4><b>{% trans "Past Timers" %}</b></h4>
<table class="table table-bordered"> {% if past_timers %}
<table class="table">
<tr> <tr>
<th style="width:150px" class="text-center">{% trans "Details" %}</th> <th style="width:150px" class="text-center">{% trans "Details" %}</th>
<th class="text-center">{% trans "Objective" %}</th> <th class="text-center">{% trans "Objective" %}</th>
@ -320,6 +325,9 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<div class="alert alert-warning text-center">No past timers.</div>
{% endif %}
</div> </div>