mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 06:06:19 +01:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00fe2a527e | ||
|
|
f53ec3b43e | ||
|
|
4d0417f114 | ||
|
|
00903b64db | ||
|
|
a3038cad00 | ||
|
|
ef99f1afac | ||
|
|
cc00d4bd04 | ||
|
|
250f26ff6f | ||
|
|
62b786ca4a | ||
|
|
9cfb47e658 | ||
|
|
ccef27b637 | ||
|
|
8dee61fd39 | ||
|
|
ae64bd0e19 | ||
|
|
a75e93dbfc | ||
|
|
0aa66c5729 | ||
|
|
4c2434219d | ||
|
|
8c65fda33b | ||
|
|
14f2751bbb | ||
|
|
d37a543c39 | ||
|
|
4947e0c483 | ||
|
|
f87c630b86 | ||
|
|
73789ea734 | ||
|
|
5a16c9c495 | ||
|
|
9dd8357f67 | ||
|
|
623e77a268 | ||
|
|
73403b98df | ||
|
|
7aa1a2f336 | ||
|
|
08e42d2f56 | ||
|
|
69248fd7bb | ||
|
|
0af188c6ab | ||
|
|
8b6d32d0d1 | ||
|
|
3c11c25d69 | ||
|
|
12e6cc63e8 | ||
|
|
d429c8b59a | ||
|
|
ddd7a3551b | ||
|
|
49ede92e06 | ||
|
|
b813213328 | ||
|
|
14065b3ca9 | ||
|
|
41f9dc490a | ||
|
|
48d25ca73f | ||
|
|
e49e04034c | ||
|
|
c7860f8e5c | ||
|
|
adb982114a | ||
|
|
5b8983deac | ||
|
|
1730bc3b98 | ||
|
|
4374064d98 | ||
|
|
c1d7994045 | ||
|
|
7bda367cc8 | ||
|
|
3de7a2ccd2 | ||
|
|
9cc278df31 | ||
|
|
a0bab07e2f | ||
|
|
149bbd92ca | ||
|
|
1de3c989d7 | ||
|
|
2e547945e2 | ||
|
|
4d4a9a27af | ||
|
|
5b41d0995f | ||
|
|
ab98d72022 | ||
|
|
8a7cd3f74d | ||
|
|
35cb56d6e9 | ||
|
|
a7a2ffd16b | ||
|
|
dbeda324e0 | ||
|
|
bf1fe99d98 | ||
|
|
41429ec7c7 | ||
|
|
c8ad1dcc7a | ||
|
|
0b7520e3b1 | ||
|
|
48c8ccfe97 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -62,3 +62,5 @@ celerybeat-schedule
|
||||
|
||||
#pycharm
|
||||
.idea/*
|
||||
|
||||
/nbproject/
|
||||
|
||||
41
.gitlab-ci.yml
Normal file
41
.gitlab-ci.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
# Official language image. Look for the different tagged releases at:
|
||||
# https://hub.docker.com/r/library/python/tags/
|
||||
|
||||
.job_template: &job_definition
|
||||
# Change pip's cache directory to be inside the project directory since we can
|
||||
# only cache local items.
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache"
|
||||
|
||||
# Pip's cache doesn't store the python packages
|
||||
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
|
||||
#
|
||||
# If you want to also cache the installed packages, you have to install
|
||||
# them in a virtualenv and cache it as well.
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
- venv/
|
||||
|
||||
before_script:
|
||||
- python -V # Print out python version for debugging
|
||||
- pip install virtualenv tox
|
||||
- virtualenv venv
|
||||
- source venv/bin/activate
|
||||
|
||||
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'
|
||||
|
||||
|
||||
py36-dj111:
|
||||
<<: *job_definition
|
||||
image: python:3.6-stretch
|
||||
script:
|
||||
- export TOXENV=py36-dj111
|
||||
- tox
|
||||
|
||||
py36-dj20:
|
||||
<<: *job_definition
|
||||
image: python:3.6-stretch
|
||||
script:
|
||||
- export TOXENV=py36-dj20
|
||||
- tox
|
||||
15
README.md
15
README.md
@@ -3,8 +3,9 @@ Alliance Auth
|
||||
|
||||
[](https://gitter.im/R4stl1n/allianceauth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://allianceauth.readthedocs.io/?badge=latest)
|
||||
[](https://travis-ci.org/allianceauth/allianceauth)
|
||||
[](https://coveralls.io/github/allianceauth/allianceauth?branch=master)
|
||||
[](https://gitlab.com/allianceauth/allianceauth/commits/master)
|
||||
[](https://gitlab.com/allianceauth/allianceauth/commits/master)
|
||||
|
||||
|
||||
|
||||
An auth system for EVE Online to help in-game organizations manage online service access.
|
||||
@@ -16,13 +17,13 @@ An auth system for EVE Online to help in-game organizations manage online servic
|
||||
|
||||
Active Developers:
|
||||
|
||||
- [Adarnof](https://github.com/adarnof/)
|
||||
- [Basraah](https://github.com/basraah/)
|
||||
- [Adarnof](https://gitlab.com/adarnof/)
|
||||
- [Basraah](https://gitlab.com/basraah/)
|
||||
|
||||
Beta Testers / Bug Fixers:
|
||||
|
||||
- [ghoti](https://github.com/ghoti/)
|
||||
- [mmolitor87](https://github.com/mmolitor87/)
|
||||
- [ghoti](https://gitlab.com/ChainsawMcGinny/)
|
||||
- [mmolitor87](https://gitlab.com/mmolitor87/)
|
||||
- [TargetZ3R0](https://github.com/TargetZ3R0)
|
||||
- [kaezon](https://github.com/kaezon/)
|
||||
- [orbitroom](https://github.com/orbitroom/)
|
||||
@@ -31,4 +32,4 @@ Beta Testers / Bug Fixers:
|
||||
Special thanks to [Nikdoof](https://github.com/nikdoof/), as his [auth](https://github.com/nikdoof/test-auth) was the foundation for the original work on this project.
|
||||
|
||||
### Contributing
|
||||
Make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at [https://developers.eveonline.com](https://developers.eveonline.com) before submitting any pull requests.
|
||||
Make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at [https://developers.eveonline.com](https://developers.eveonline.com) before submitting any pull requests.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This will make sure the app is always imported when
|
||||
# Django starts so that shared_task will use this app.
|
||||
|
||||
__version__ = '2.0.4'
|
||||
__version__ = '2.1.0'
|
||||
NAME = 'Alliance Auth v%s' % __version__
|
||||
default_app_config = 'allianceauth.apps.AllianceAuthConfig'
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from django.conf.urls import include
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from functools import wraps
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib import messages
|
||||
@@ -35,3 +37,32 @@ def main_character_required(view_func):
|
||||
messages.error(request, _('A main character is required to perform that action. Add one below.'))
|
||||
return redirect('authentication:dashboard')
|
||||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def permissions_required(perm, login_url=None, raise_exception=False):
|
||||
"""
|
||||
Decorator for views that checks whether a user has a particular permission
|
||||
enabled, redirecting to the log-in page if necessary.
|
||||
If the raise_exception parameter is given the PermissionDenied exception
|
||||
is raised.
|
||||
|
||||
This decorator is identical to the django permission_required except it
|
||||
allows for passing a tuple/list of perms that will return true if any one
|
||||
of them is present.
|
||||
"""
|
||||
def check_perms(user):
|
||||
if isinstance(perm, str):
|
||||
perms = (perm,)
|
||||
else:
|
||||
perms = perm
|
||||
# First check if the user has the permission (even anon users)
|
||||
for perm_ in perms:
|
||||
perm_ = (perm_,)
|
||||
if user.has_perms(perm_):
|
||||
return True
|
||||
# In case the 403 handler should be called raise the exception
|
||||
if raise_exception:
|
||||
raise PermissionDenied
|
||||
# As the last resort, show the login form
|
||||
return False
|
||||
return user_passes_test(check_perms, login_url=login_url)
|
||||
|
||||
@@ -16,10 +16,16 @@ class CorpStatsQuerySet(models.QuerySet):
|
||||
assert char
|
||||
# build all accepted queries
|
||||
queries = [models.Q(token__user=user)]
|
||||
if user.has_perm('corputils.view_corp_corpstats'):
|
||||
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||
if user.has_perm('corputils.view_alliance_corpstats'):
|
||||
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
|
||||
if char.alliance_id is not None:
|
||||
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
|
||||
else:
|
||||
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||
if user.has_perm('corputils.view_corp_corpstats'):
|
||||
if user.has_perm('corputils.view_alliance_corpstats'):
|
||||
pass
|
||||
else:
|
||||
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||
if user.has_perm('corputils.view_state_corpstats'):
|
||||
queries.append(models.Q(corp__in=user.profile.state.member_corporations.all()))
|
||||
queries.append(models.Q(corp__alliance__in=user.profile.state.member_alliances.all()))
|
||||
|
||||
@@ -15,9 +15,11 @@ SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sw
|
||||
"""
|
||||
Swagger spec operations:
|
||||
|
||||
Character
|
||||
get_characters_character_id
|
||||
get_corporations_corporation_id_members
|
||||
get_characters_names
|
||||
Universe
|
||||
post_universe_names
|
||||
"""
|
||||
|
||||
|
||||
@@ -55,11 +57,11 @@ class CorpStats(models.Model):
|
||||
# the swagger spec doesn't have a maxItems count
|
||||
# manual testing says we can do over 350, but let's not risk it
|
||||
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
||||
member_name_chunks = [c.Character.get_characters_names(character_ids=id_chunk).result() for id_chunk in
|
||||
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in
|
||||
member_id_chunks]
|
||||
member_list = {}
|
||||
for name_chunk in member_name_chunks:
|
||||
member_list.update({m['character_id']: m['character_name'] for m in name_chunk})
|
||||
member_list.update({m['id']: m['name'] for m in name_chunk})
|
||||
|
||||
# bulk create new member models
|
||||
missing_members = [m_id for m_id in member_ids if
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -86,7 +86,7 @@ class CorpStatsUpdateTestCase(TestCase):
|
||||
def test_update_add_member(self, SwaggerClient):
|
||||
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||
SwaggerClient.from_spec.return_value.Character.get_characters_names.return_value.result.return_value = [{'character_id': 1, 'character_name': 'test character'}]
|
||||
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||
self.corpstats.update()
|
||||
self.assertTrue(CorpMember.objects.filter(character_id='1', character_name='test character', corpstats=self.corpstats).exists())
|
||||
|
||||
@@ -95,7 +95,7 @@ class CorpStatsUpdateTestCase(TestCase):
|
||||
CorpMember.objects.create(character_id='2', character_name='old test character', corpstats=self.corpstats)
|
||||
SwaggerClient.from_spec.return_value.Character.get_characters_character_id.return_value.result.return_value = {'corporation_id': 2}
|
||||
SwaggerClient.from_spec.return_value.Corporation.get_corporations_corporation_id_members.return_value.result.return_value = [1]
|
||||
SwaggerClient.from_spec.return_value.Character.get_characters_names.return_value.result.return_value = [{'character_id': 1, 'character_name': 'test character'}]
|
||||
SwaggerClient.from_spec.return_value.Universe.post_universe_names.return_value.result.return_value = [{'id': 1, 'name': 'test character'}]
|
||||
self.corpstats.update()
|
||||
self.assertFalse(CorpMember.objects.filter(character_id='2', corpstats=self.corpstats).exists())
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.0.6 on 2018-08-03 04:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fleetactivitytracking', '0005_remove_fat_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='fat',
|
||||
name='shiptype',
|
||||
field=models.CharField(max_length=100),
|
||||
),
|
||||
]
|
||||
@@ -24,7 +24,7 @@ class Fat(models.Model):
|
||||
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
|
||||
fatlink = models.ForeignKey(Fatlink, on_delete=models.CASCADE)
|
||||
system = models.CharField(max_length=30)
|
||||
shiptype = models.CharField(max_length=30)
|
||||
shiptype = models.CharField(max_length=100)
|
||||
station = models.CharField(max_length=125)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-lg-6">
|
||||
<div class="table-responsive">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover table-striped">
|
||||
<tr>
|
||||
<th class="col-md-1"></th>
|
||||
@@ -26,7 +26,7 @@
|
||||
{% for char_name, user_id in member_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="http://image.eveonline.com/Character/{{ user_id.char_id }}_32.jpg" class="img-circle">
|
||||
<img src="https://imageserver.eveonline.com/Character/{{ user_id.char_id }}_32.jpg" class="img-circle">
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ user_id.char_name }}</p>
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
{% include "fleetup/menu.html" %}
|
||||
<div class="panel">
|
||||
<div>
|
||||
{% for a, j in doctrine.items %}
|
||||
{% regroup j.Data|dictsort:"Role" by Role as role_list %}
|
||||
|
||||
{% for Role in role_list %}
|
||||
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><b>{{ Role.grouper }}</b></h3>
|
||||
@@ -50,7 +50,7 @@
|
||||
{% load humanize %}{{ item.EstPrice|intword }}
|
||||
</td>
|
||||
<td>
|
||||
{% for categories in item.Categories %}
|
||||
{% for categories in item.Categories %}
|
||||
{{ categories }},
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
{% include "fleetup/menu.html" %}
|
||||
<div class="panel">
|
||||
<div>
|
||||
{% if doctrines_list %}
|
||||
{% for a, j in doctrines_list.items %}
|
||||
{% regroup j|dictsort:"FolderName" by FolderName as folder_list %}
|
||||
{% for FolderName in folder_list %}
|
||||
<div class="col-lg-8">
|
||||
<div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><b>{{ FolderName.grouper }}</b></h3>
|
||||
@@ -29,11 +29,11 @@
|
||||
<th class="col-lg-2">Note</th>-->
|
||||
</tr>
|
||||
{% for item in FolderName.list %}
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'fleetup:doctrine' item.DoctrineId %}"><img src="https://image.eveonline.com/InventoryType/{{ item.IconId }}_32.png"></a>
|
||||
</td>
|
||||
</td>
|
||||
<td>
|
||||
{{ item.Name }}
|
||||
</td>
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
{% include "fleetup/menu.html" %}
|
||||
<div class="tab-content">
|
||||
<div class="tab-content row">
|
||||
<div id="fit" class="tab-pane fade in active">
|
||||
<div class="col-lg-3">
|
||||
<div class="col-lg-4">
|
||||
{% for x, y in fitting_data.items %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
@@ -18,22 +18,24 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% for doctrin in y.Doctrines %}
|
||||
<h4>{{ doctrin.Name }}</h4>
|
||||
<div class="col-lg-12">
|
||||
<p>{% trans "Role in doctrine:" %} {{ doctrin.Role }}</p>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<p>{% trans "Priority:" %}</p>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="{{ doctrin.Priority }}" aria-valuemin="0" aria-valuemax="5" style="width: {% widthratio doctrin.Priority 5 100 %}%;">
|
||||
{{ doctrin.Priority }}/5
|
||||
<div class="clearfix">
|
||||
<h4>{{ doctrin.Name }}</h4>
|
||||
<div class="col-lg-12">
|
||||
<p>{% trans "Role in doctrine:" %} {{ doctrin.Role }}</p>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<p>{% trans "Priority:" %}</p>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="{{ doctrin.Priority }}" aria-valuemin="0" aria-valuemax="5" style="width: {% widthratio doctrin.Priority 5 100 %}%;">
|
||||
{{ doctrin.Priority }}/5
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary" href="{% url 'fleetup:doctrine' doctrin.DoctrineId %}">{% trans "See doctrine" %}</a>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary" href="{% url 'fleetup:doctrine' doctrin.DoctrineId %}">{% trans "See doctrine" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -56,10 +58,10 @@
|
||||
<div class="panel-body">
|
||||
<div class="list-group">
|
||||
{% for arbit, orbit in doctrines_list.items %}
|
||||
|
||||
|
||||
{% for fitting in orbit.Data %}
|
||||
<a href="{% url 'fleetup:fitting' fitting.FittingId %}" class="list-group-item">
|
||||
|
||||
|
||||
<h4 class="list-group-item-heading">{{ fitting.Name }}<span class="pull-right"><img src="https://image.eveonline.com/InventoryType/{{ fitting.EveTypeId }}_32.png" class="img-circle"></span></h4>
|
||||
<p class="list-group-item-heading">{{ fitting.Role }} - {{ fitting.ShipType }}</p>
|
||||
</a>
|
||||
@@ -107,8 +109,8 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3">
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "EFT/Export" %}</h3>
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
<th class="col-md-2">{% trans "Categories" %}</th>
|
||||
</tr>
|
||||
{% for id, fittings in fitting_list %}
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'fleetup:fitting' fittings.fitting_id %}"><img src="https://image.eveonline.com/InventoryType/{{ fittings.icon_id }}_32.png"></a>
|
||||
</td>
|
||||
</td>
|
||||
<td>
|
||||
{{ fittings.name }}
|
||||
</td>
|
||||
@@ -38,12 +38,12 @@
|
||||
{% load humanize %}{{ fittings.estimated|intword }}
|
||||
</td>
|
||||
<td>
|
||||
{% for categories in fittings.categories %}
|
||||
{% for categories in fittings.categories %}
|
||||
{{ categories }},
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
|
||||
@@ -1,256 +1,254 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}FleetUp{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
{% include "fleetup/menu.html" %}
|
||||
<div class="panel">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#operations">{% trans "Operations" %}</a></li>
|
||||
<li><a data-toggle="tab" href="#timers">{% trans "Timers" %}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div id="operations" class="tab-pane fade in active">
|
||||
<div class="col-lg-7">
|
||||
{% if operations_list %}
|
||||
{% for subject, start in operations_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><b>{{ start.subject }}</b></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<th class="col-md-6">{% trans "Start" %}</th>
|
||||
<th class="col-md-6">{% trans "End" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6">{{ start.start|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
||||
|
||||
<td class="col-md-6">{{ start.end|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6">
|
||||
<span id="localtime{{ start.operation_id }}"></span> <span class='label label-success'>Local time</span><br>
|
||||
<div id="countdown{{ start.operation_id }}"></div>
|
||||
</td>
|
||||
|
||||
<td class="col-md-6"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>{{ start.details }}</p>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th class="col-md-4">{% trans "Location" %}</th>
|
||||
<th class="col-md-4">{% trans "Doctrine" %}</th>
|
||||
<th class="col-md-2">{% trans "Organizer" %}</th>
|
||||
<th class="col-md-2">{% trans "URL" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ start.location }} - {{ start.location_info }} <a href="http://evemaps.dotlan.net/system/{{ start.location }}" target="_blank" class="label label-success">Dotlan</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if start.doctrine %}
|
||||
{% for doctrine in start.doctrine %}
|
||||
|
||||
<a href="{% url 'fleetup:doctrine' doctrine.Id %}" class="label label-success">{{ doctrine.Name }}</a>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "TBA" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ start.organizer }}
|
||||
</td>
|
||||
<td>
|
||||
{% ifequal start.url "" %}
|
||||
<div class="label label-danger">{% trans "No link" %}</div>
|
||||
{% else %}
|
||||
<a href="{{ start.url }}" target="_blank" class="label label-success">{% trans "External link" %}</a>
|
||||
{% endifequal %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<h3>{% trans "There seems to be no Operations in the near future." %}</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Current Eve Time:" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="current-time"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% if timers_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-hover table-striped">
|
||||
{% for notes, type in timers_list %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ type.solarsystem }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.expires|date:"l d M H:i" }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="timers" class="tab-pane fade in">
|
||||
<div class="col-lg-12">
|
||||
{% if timers_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-condensed table-hover table-striped">
|
||||
<tr>
|
||||
<th class="col-lg-1">{% trans "Type" %}</th>
|
||||
<th class="col-lg-1">{% trans "Structure" %}</th>
|
||||
<th class="col-lg-2">{% trans "Location" %}</th>
|
||||
<th class="col-lg-2">{% trans "Expires(EVE-time)" %}</th>
|
||||
<th class="col-lg-1">{% trans "Owner" %}</th>
|
||||
<th class="col-lg-2">{% trans "Note" %}</th>
|
||||
</tr>
|
||||
{% for notes, type in timers_list %}
|
||||
<tr>
|
||||
<td>
|
||||
{% ifequal type.type "Final" %}
|
||||
<span class="label label-danger">
|
||||
{{ type.type }}</span>{% else %}{{ type.type }}{% endifequal %}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.timer_type }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.solarsystem }} - Planet:{{ type.planet }} Moon:{{ type.moon }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.expires|date:"l d M H:i" }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.owner }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.notes }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3>{% trans "There seems to be no Timers in the near future." %}</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% include 'bundles/moment-js.html' with locale=True %}
|
||||
<script src="{% static 'js/timers.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
// Data
|
||||
var timers = [
|
||||
{% for start, op in operations_list %}
|
||||
{
|
||||
'id': {{ op.operation_id }},
|
||||
'start': moment("{{ op.start | date:"c" }}"),
|
||||
'end': moment("{{ op.end | date:"c" }}"),
|
||||
'expired': false
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
timedUpdate();
|
||||
setAllLocalTimes();
|
||||
|
||||
// Start timed updates
|
||||
setInterval(timedUpdate, 1000);
|
||||
|
||||
function timedUpdate() {
|
||||
updateClock();
|
||||
updateAllTimers();
|
||||
}
|
||||
|
||||
function updateAllTimers () {
|
||||
var l = timers.length;
|
||||
for (var i=0; i < l; ++i) {
|
||||
if (timers[i].expired) continue;
|
||||
updateTimer(timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a timer
|
||||
* @param timer Timer information
|
||||
* @param timer.start Date of the timer
|
||||
* @param timer.id Id number of the timer
|
||||
* @param timer.expired
|
||||
*/
|
||||
function updateTimer(timer) {
|
||||
if (timer.start.isAfter(Date.now())) {
|
||||
var duration = moment.duration(timer.start - moment(), 'milliseconds');
|
||||
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
|
||||
} else {
|
||||
timer.expired = true;
|
||||
document.getElementById("countdown" + timer.id).innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all local time fields
|
||||
*/
|
||||
function setAllLocalTimes() {
|
||||
var l = timers.length;
|
||||
for (var i=0; i < l; ++i) {
|
||||
setLocalTime(timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the local time info for the timer
|
||||
* @param timer Timer information
|
||||
* @param timer.start Date of the timer
|
||||
* @param timer.id Id number of the timer
|
||||
*/
|
||||
function setLocalTime(timer) {
|
||||
document.getElementById("localtime" + timer.id).innerHTML = timer.start.format("ddd @ LT");
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
document.getElementById("current-time").innerHTML = "<b>" + moment.utc().format('ddd, ll HH:mm:ss z') + "</b>";
|
||||
}
|
||||
</script>
|
||||
{% endblock content %}
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}FleetUp{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
{% include "fleetup/menu.html" %}
|
||||
<div>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#operations">{% trans "Operations" %}</a></li>
|
||||
<li><a data-toggle="tab" href="#timers">{% trans "Timers" %}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content row">
|
||||
<div id="operations" class="tab-pane fade in active">
|
||||
<div class="col-lg-8">
|
||||
{% if operations_list %}
|
||||
{% for subject, start in operations_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><b>{{ start.subject }}</b></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<th class="col-md-6">{% trans "Start" %}</th>
|
||||
<th class="col-md-6">{% trans "End" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6">{{ start.start|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
||||
|
||||
<td class="col-md-6">{{ start.end|date:"l d M H:i" }} <span class="label label-success">{% trans "Eve Time" %}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6">
|
||||
<span id="localtime{{ start.operation_id }}"></span> <span class='label label-success'>Local time</span><br>
|
||||
<div id="countdown{{ start.operation_id }}"></div>
|
||||
</td>
|
||||
|
||||
<td class="col-md-6"></td>
|
||||
</tr>
|
||||
</table>
|
||||
{{ start.details|linebreaks }}
|
||||
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th class="col-md-4">{% trans "Location" %}</th>
|
||||
<th class="col-md-4">{% trans "Doctrine" %}</th>
|
||||
<th class="col-md-2">{% trans "Organizer" %}</th>
|
||||
<th class="col-md-2">{% trans "URL" %}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ start.location }} - {{ start.location_info }} <a href="http://evemaps.dotlan.net/system/{{ start.location }}" target="_blank" class="label label-success">Dotlan</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if start.doctrine %}
|
||||
{% for doctrine in start.doctrine %}
|
||||
|
||||
<a href="{% url 'fleetup:doctrine' doctrine.Id %}" class="label label-success">{{ doctrine.Name }}</a>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "TBA" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ start.organizer }}
|
||||
</td>
|
||||
<td>
|
||||
{% ifequal start.url "" %}
|
||||
<div class="label label-danger">{% trans "No link" %}</div>
|
||||
{% else %}
|
||||
<a href="{{ start.url }}" target="_blank" class="label label-success">{% trans "External link" %}</a>
|
||||
{% endifequal %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<h3>{% trans "There seems to be no Operations in the near future." %}</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Current Eve Time:" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="current-time"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% if timers_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-hover table-striped">
|
||||
{% for notes, type in timers_list %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ type.solarsystem }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.expires|date:"l d M H:i" }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="timers" class="tab-pane fade in">
|
||||
<div class="col-lg-12">
|
||||
{% if timers_list %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">{% trans "Timers" %}</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-condensed table-hover table-striped">
|
||||
<tr>
|
||||
<th class="col-lg-1">{% trans "Type" %}</th>
|
||||
<th class="col-lg-1">{% trans "Structure" %}</th>
|
||||
<th class="col-lg-2">{% trans "Location" %}</th>
|
||||
<th class="col-lg-2">{% trans "Expires(EVE-time)" %}</th>
|
||||
<th class="col-lg-1">{% trans "Owner" %}</th>
|
||||
<th class="col-lg-2">{% trans "Note" %}</th>
|
||||
</tr>
|
||||
{% for notes, type in timers_list %}
|
||||
<tr>
|
||||
<td>
|
||||
{% ifequal type.type "Final" %}
|
||||
<span class="label label-danger">
|
||||
{{ type.type }}</span>{% else %}{{ type.type }}{% endifequal %}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.timer_type }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.solarsystem }} - Planet:{{ type.planet }} Moon:{{ type.moon }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.expires|date:"l d M H:i" }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.owner }}
|
||||
</td>
|
||||
<td>
|
||||
{{ type.notes }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3>{% trans "There seems to be no Timers in the near future." %}</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% include 'bundles/moment-js.html' with locale=True %}
|
||||
<script src="{% static 'js/timers.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
// Data
|
||||
var timers = [
|
||||
{% for start, op in operations_list %}
|
||||
{
|
||||
'id': {{ op.operation_id }},
|
||||
'start': moment("{{ op.start | date:"c" }}"),
|
||||
'end': moment("{{ op.end | date:"c" }}"),
|
||||
'expired': false
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
timedUpdate();
|
||||
setAllLocalTimes();
|
||||
|
||||
// Start timed updates
|
||||
setInterval(timedUpdate, 1000);
|
||||
|
||||
function timedUpdate() {
|
||||
updateClock();
|
||||
updateAllTimers();
|
||||
}
|
||||
|
||||
function updateAllTimers () {
|
||||
var l = timers.length;
|
||||
for (var i=0; i < l; ++i) {
|
||||
if (timers[i].expired) continue;
|
||||
updateTimer(timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a timer
|
||||
* @param timer Timer information
|
||||
* @param timer.start Date of the timer
|
||||
* @param timer.id Id number of the timer
|
||||
* @param timer.expired
|
||||
*/
|
||||
function updateTimer(timer) {
|
||||
if (timer.start.isAfter(Date.now())) {
|
||||
var duration = moment.duration(timer.start - moment(), 'milliseconds');
|
||||
document.getElementById("countdown" + timer.id).innerHTML = getDurationString(duration);
|
||||
} else {
|
||||
timer.expired = true;
|
||||
document.getElementById("countdown" + timer.id).innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all local time fields
|
||||
*/
|
||||
function setAllLocalTimes() {
|
||||
var l = timers.length;
|
||||
for (var i=0; i < l; ++i) {
|
||||
setLocalTime(timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the local time info for the timer
|
||||
* @param timer Timer information
|
||||
* @param timer.start Date of the timer
|
||||
* @param timer.id Id number of the timer
|
||||
*/
|
||||
function setLocalTime(timer) {
|
||||
document.getElementById("localtime" + timer.id).innerHTML = timer.start.format("ddd @ LT");
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
document.getElementById("current-time").innerHTML = "<b>" + moment.utc().format('ddd, ll HH:mm:ss z') + "</b>";
|
||||
}
|
||||
</script>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -8,8 +8,8 @@ from .models import GroupRequest
|
||||
|
||||
class AuthGroupInlineAdmin(admin.StackedInline):
|
||||
model = AuthGroup
|
||||
filter_horizontal = ('group_leaders',)
|
||||
fields = ('description', 'group_leaders', 'internal', 'hidden', 'open', 'public')
|
||||
filter_horizontal = ('group_leaders', 'states',)
|
||||
fields = ('description', 'group_leaders', 'states', 'internal', 'hidden', 'open', 'public')
|
||||
verbose_name_plural = 'Auth Settings'
|
||||
verbose_name = ''
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
class GroupManager:
|
||||
@@ -6,7 +7,12 @@ class GroupManager:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_joinable_groups():
|
||||
def get_joinable_groups(state):
|
||||
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)\
|
||||
.filter(Q(authgroup__states=state) | Q(authgroup__states=None))
|
||||
|
||||
@staticmethod
|
||||
def get_all_non_internal_groups():
|
||||
return Group.objects.select_related('authgroup').exclude(authgroup__internal=True)
|
||||
|
||||
@staticmethod
|
||||
@@ -14,13 +20,35 @@ class GroupManager:
|
||||
return Group.objects.select_related('authgroup').filter(authgroup__group_leaders__in=[user])
|
||||
|
||||
@staticmethod
|
||||
def joinable_group(group):
|
||||
def joinable_group(group, state):
|
||||
"""
|
||||
Check if a group is a user joinable group, i.e.
|
||||
not an internal group for Corp, Alliance, Members etc
|
||||
Check if a group is a user/state joinable group, i.e.
|
||||
not an internal group for Corp, Alliance, Members etc,
|
||||
or restricted from the user's current state.
|
||||
:param group: django.contrib.auth.models.Group object
|
||||
:param state: allianceauth.authentication.State object
|
||||
:return: bool True if its joinable, False otherwise
|
||||
"""
|
||||
if len(group.authgroup.states.all()) != 0 and state not in group.authgroup.states.all():
|
||||
return False
|
||||
return not group.authgroup.internal
|
||||
|
||||
@staticmethod
|
||||
def check_internal_group(group):
|
||||
"""
|
||||
Check if a group is auditable, i.e not an internal group
|
||||
:param group: django.contrib.auth.models.Group object
|
||||
:return: bool True if it is auditable, false otherwise
|
||||
"""
|
||||
return not group.authgroup.internal
|
||||
|
||||
@staticmethod
|
||||
def check_internal_group(group):
|
||||
"""
|
||||
Check if a group is auditable, i.e not an internal group
|
||||
:param group: django.contrib.auth.models.Group object
|
||||
:return: bool True if it is auditable, false otherwise
|
||||
"""
|
||||
return not group.authgroup.internal
|
||||
|
||||
@staticmethod
|
||||
|
||||
28
allianceauth/groupmanagement/migrations/0009_requestlog.py
Normal file
28
allianceauth/groupmanagement/migrations/0009_requestlog.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 2.0.6 on 2018-06-04 02:45
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('groupmanagement', '0008_remove_authgroup_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RequestLog',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('request_type', models.NullBooleanField(default=0)),
|
||||
('request_info', models.CharField(max_length=254)),
|
||||
('action', models.BooleanField(default=0)),
|
||||
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||
('request_actor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.0.6 on 2018-07-11 00:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0016_ownershiprecord'),
|
||||
('groupmanagement', '0009_requestlog'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='authgroup',
|
||||
name='states',
|
||||
field=models.ManyToManyField(blank=True, help_text='States listed here will have the ability to join this group provided they have the proper permissions.', related_name='valid_states', to='authentication.State'),
|
||||
),
|
||||
]
|
||||
@@ -3,6 +3,7 @@ from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from allianceauth.authentication.models import State
|
||||
|
||||
|
||||
class GroupRequest(models.Model):
|
||||
@@ -23,6 +24,37 @@ class GroupRequest(models.Model):
|
||||
return self.user.username + ":" + self.group.name
|
||||
|
||||
|
||||
class RequestLog(models.Model):
|
||||
request_type = models.NullBooleanField(default=0)
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
request_info = models.CharField(max_length=254)
|
||||
action = models.BooleanField(default=0)
|
||||
request_actor = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
def requestor(self):
|
||||
return self.request_info.split(":")[0]
|
||||
|
||||
def type_to_str(self):
|
||||
if self.request_type is None:
|
||||
return "Removed"
|
||||
elif self.request_type is True:
|
||||
return "Leave"
|
||||
elif self.request_type is False:
|
||||
return "Join"
|
||||
|
||||
def action_to_str(self):
|
||||
if self.action is True:
|
||||
return "Accept"
|
||||
elif self.action is False:
|
||||
return "Reject"
|
||||
|
||||
def req_char(self):
|
||||
usr = self.requestor()
|
||||
user = User.objects.get(username=usr)
|
||||
return user.profile.main_character
|
||||
|
||||
|
||||
|
||||
class AuthGroup(models.Model):
|
||||
"""
|
||||
Extends Django Group model with a one-to-one field
|
||||
@@ -65,6 +97,10 @@ class AuthGroup(models.Model):
|
||||
"specifically. Use the auth.group_management permission to allow "
|
||||
"a user to manage all groups.")
|
||||
|
||||
states = models.ManyToManyField(State, related_name='valid_states', blank=True,
|
||||
help_text="States listed here will have the ability to join this group provided "
|
||||
"they have the proper permissions.")
|
||||
|
||||
description = models.CharField(max_length=512, blank=True, help_text="Description of the group shown to users.")
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends "allianceauth/base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{{ group }} {% trans "Audit Log" %}{% endblock page_title %}
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<br>
|
||||
{% include 'groupmanagement/menu.html' %}
|
||||
<div>
|
||||
{% if entries %}
|
||||
<h3>{{ group }} Audit Log</h3>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="text-center">{% trans "Requestor" %}</th>
|
||||
<th class="text-center">{% trans "Main Character" %}</th>
|
||||
<th class="text-center">{% trans "Group" %}</th>
|
||||
<th class="text-center">{% trans "Type" %}</th>
|
||||
<th class="text-center">{% trans "Action" %}</th>
|
||||
<th class="text-center">{% trans "Actor" %}</th>
|
||||
</tr>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td class="text-center">{{ entry.requestor }}</td>
|
||||
<td class="text-center">{{ entry.req_char }}</td>
|
||||
<td class="text-center">{{ entry.group }}</td>
|
||||
<td class="text-center">{{ entry.type_to_str }}</td>
|
||||
<td class="text-center">{{ entry.action_to_str }}</td>
|
||||
<td class="text-center">{{ entry.request_actor }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% trans "No entries found." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -41,6 +41,9 @@
|
||||
title="{% trans "View Members" %}">
|
||||
<i class="glyphicon glyphicon-eye-open"></i>
|
||||
</a>
|
||||
<a href="{% url "groupmanagement:audit_log" group.id %}" class="btn btn-info" title="{% trans "Audit Members" %}">
|
||||
<i class="glyphicon glyphicon-list-alt"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -12,6 +12,7 @@ urlpatterns = [
|
||||
name='membership'),
|
||||
url(r'^membership/(\w+)/$', views.group_membership_list,
|
||||
name='membership_list'),
|
||||
url(r'^membership/(\w+)/audit/', views.group_membership_audit, name="audit_log"),
|
||||
url(r'^membership/(\w+)/remove/(\w+)/$', views.group_membership_remove,
|
||||
name='membership_remove'),
|
||||
url(r'^request_add/(\w+)', views.group_request_add,
|
||||
|
||||
@@ -10,10 +10,12 @@ from django.http import Http404
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from .managers import GroupManager
|
||||
from .models import GroupRequest
|
||||
from .models import GroupRequest, RequestLog
|
||||
|
||||
from allianceauth.notifications import notify
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -53,7 +55,7 @@ def group_membership(request):
|
||||
# Get all open and closed groups
|
||||
if GroupManager.has_management_permission(request.user):
|
||||
# Full access
|
||||
groups = GroupManager.get_joinable_groups()
|
||||
groups = GroupManager.get_all_non_internal_groups()
|
||||
else:
|
||||
# Group leader specific
|
||||
groups = GroupManager.get_group_leaders_groups(request.user)
|
||||
@@ -65,6 +67,32 @@ def group_membership(request):
|
||||
return render(request, 'groupmanagement/groupmembership.html', context=render_items)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_membership_audit(request, group_id):
|
||||
logger.debug("group_management_audit called by user %s" % request.user)
|
||||
group = get_object_or_404(Group, id=group_id)
|
||||
try:
|
||||
|
||||
# Check its a joinable group i.e. not corp or internal
|
||||
# And the user has permission to manage it
|
||||
if not GroupManager.check_internal_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
logger.warning("User %s attempted to view the membership of group %s but permission was denied" %
|
||||
(request.user, group_id))
|
||||
raise PermissionDenied
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404("Group does not exist")
|
||||
|
||||
entries = RequestLog.objects.filter(group=group)
|
||||
|
||||
render_items = {'entries': entries, 'group': group.name}
|
||||
|
||||
return render(request, 'groupmanagement/audit.html', context=render_items)
|
||||
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_membership_list(request, group_id):
|
||||
@@ -74,7 +102,7 @@ def group_membership_list(request, group_id):
|
||||
|
||||
# Check its a joinable group i.e. not corp or internal
|
||||
# And the user has permission to manage it
|
||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
if not GroupManager.check_internal_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
logger.warning("User %s attempted to view the membership of group %s but permission was denied" %
|
||||
(request.user, group_id))
|
||||
raise PermissionDenied
|
||||
@@ -105,13 +133,16 @@ def group_membership_remove(request, group_id, user_id):
|
||||
try:
|
||||
# Check its a joinable group i.e. not corp or internal
|
||||
# And the user has permission to manage it
|
||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
if not GroupManager.check_internal_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
logger.warning("User %s attempted to remove a user from group %s but permission was denied" % (request.user,
|
||||
group_id))
|
||||
raise PermissionDenied
|
||||
|
||||
try:
|
||||
user = group.user_set.get(id=user_id)
|
||||
request_info = user.username + ":" + group.name
|
||||
log = RequestLog(request_type=None,group=group,request_info=request_info,action=1,request_actor=request.user)
|
||||
log.save()
|
||||
# Remove group from user
|
||||
user.groups.remove(group)
|
||||
logger.info("User %s removed user %s from group %s" % (request.user, user, group))
|
||||
@@ -133,12 +164,14 @@ def group_accept_request(request, group_request_id):
|
||||
try:
|
||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||
|
||||
if not GroupManager.joinable_group(group_request.group) or \
|
||||
if not GroupManager.joinable_group(group_request.group, group_request.user.profile.state) or \
|
||||
not GroupManager.can_manage_group(request.user, group_request.group):
|
||||
raise PermissionDenied
|
||||
|
||||
group_request.user.groups.add(group)
|
||||
group_request.user.save()
|
||||
log = RequestLog(request_type=group_request.leave_request,group=group,request_info=group_request.__str__(),action=1,request_actor=request.user)
|
||||
log.save()
|
||||
group_request.delete()
|
||||
logger.info("User %s accepted group request from user %s to group %s" % (
|
||||
request.user, group_request.user, group_request.group.name))
|
||||
@@ -172,6 +205,8 @@ def group_reject_request(request, group_request_id):
|
||||
if group_request:
|
||||
logger.info("User %s rejected group request from user %s to group %s" % (
|
||||
request.user, group_request.user, group_request.group.name))
|
||||
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user)
|
||||
log.save()
|
||||
group_request.delete()
|
||||
notify(group_request.user, "Group Application Rejected", level="danger",
|
||||
message="Your application to %s has been rejected." % group_request.group)
|
||||
@@ -204,6 +239,8 @@ def group_leave_accept_request(request, group_request_id):
|
||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||
group_request.user.groups.remove(group)
|
||||
group_request.user.save()
|
||||
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=1,request_actor=request.user)
|
||||
log.save()
|
||||
group_request.delete()
|
||||
logger.info("User %s accepted group leave request from user %s to group %s" % (
|
||||
request.user, group_request.user, group_request.group.name))
|
||||
@@ -236,6 +273,8 @@ def group_leave_reject_request(request, group_request_id):
|
||||
raise PermissionDenied
|
||||
|
||||
if group_request:
|
||||
log = RequestLog(request_type=group_request.leave_request,group=group_request.group,request_info=group_request.__str__(),action=0,request_actor=request.user)
|
||||
log.save()
|
||||
group_request.delete()
|
||||
logger.info("User %s rejected group leave request from user %s for group %s" % (
|
||||
request.user, group_request.user, group_request.group.name))
|
||||
@@ -262,7 +301,7 @@ def groups_view(request):
|
||||
logger.debug("groups_view called by user %s" % request.user)
|
||||
groups = []
|
||||
|
||||
group_query = GroupManager.get_joinable_groups()
|
||||
group_query = GroupManager.get_joinable_groups(request.user.profile.state)
|
||||
|
||||
if not request.user.has_perm('groupmanagement.request_groups'):
|
||||
# Filter down to public groups only for non-members
|
||||
@@ -284,11 +323,18 @@ def groups_view(request):
|
||||
def group_request_add(request, group_id):
|
||||
logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id))
|
||||
group = Group.objects.get(id=group_id)
|
||||
if not GroupManager.joinable_group(group):
|
||||
state = request.user.profile.state
|
||||
if not GroupManager.joinable_group(group, state):
|
||||
logger.warning("User %s attempted to join group id %s but it is not a joinable group" %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, _("You cannot join that group"))
|
||||
return redirect('groupmanagement:groups')
|
||||
if group in request.user.groups.all():
|
||||
# User is already a member of this group.
|
||||
logger.warning("User %s attempted to join group id %s but they are already a member." %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, "You are already a member of that group.")
|
||||
return redirect('groupmanagement:groups')
|
||||
if not request.user.has_perm('groupmanagement.request_groups') and not group.authgroup.public:
|
||||
# Does not have the required permission, trying to join a non-public group
|
||||
logger.warning("User %s attempted to join group id %s but it is not a public group" %
|
||||
@@ -299,6 +345,11 @@ def group_request_add(request, group_id):
|
||||
logger.info("%s joining %s as is an open group" % (request.user, group))
|
||||
request.user.groups.add(group)
|
||||
return redirect("groupmanagement:groups")
|
||||
req = GroupRequest.objects.filter(user=request.user, group=group)
|
||||
if len(req) > 0:
|
||||
logger.info("%s attempted to join %s but already has an open application" % (request.user, group))
|
||||
messages.warning(request, "You already have a pending application for that group.")
|
||||
return redirect("groupmanagement:groups")
|
||||
grouprequest = GroupRequest()
|
||||
grouprequest.status = _('Pending')
|
||||
grouprequest.group = group
|
||||
@@ -314,7 +365,7 @@ def group_request_add(request, group_id):
|
||||
def group_request_leave(request, group_id):
|
||||
logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id))
|
||||
group = Group.objects.get(id=group_id)
|
||||
if not GroupManager.joinable_group(group):
|
||||
if not GroupManager.check_internal_group(group):
|
||||
logger.warning("User %s attempted to leave group id %s but it is not a joinable group" %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, _("You cannot leave that group"))
|
||||
@@ -328,6 +379,15 @@ def group_request_leave(request, group_id):
|
||||
logger.info("%s leaving %s as is an open group" % (request.user, group))
|
||||
request.user.groups.remove(group)
|
||||
return redirect("groupmanagement:groups")
|
||||
req = GroupRequest.objects.filter(user=request.user, group=group)
|
||||
if len(req) > 0:
|
||||
logger.info("%s attempted to leave %s but already has an pending leave request." % (request.user, group))
|
||||
messages.warning(request, "You already have a pending leave request for that group.")
|
||||
return redirect("groupmanagement:groups")
|
||||
if getattr(settings, 'AUTO_LEAVE', False):
|
||||
logger.info("%s leaving joinable group %s due to auto_leave" % (request.user, group))
|
||||
request.user.groups.remove(group)
|
||||
return redirect('groupmanagement:groups')
|
||||
grouprequest = GroupRequest()
|
||||
grouprequest.status = _('Pending')
|
||||
grouprequest.group = group
|
||||
|
||||
@@ -355,11 +355,14 @@ class DiscourseManager:
|
||||
user_groups = DiscourseManager.__get_user_groups(username)
|
||||
add_groups = [group_dict[x] for x in group_dict if not group_dict[x] in user_groups]
|
||||
rem_groups = [x for x in user_groups if x not in inv_group_dict]
|
||||
if add_groups or rem_groups:
|
||||
if add_groups:
|
||||
logger.info(
|
||||
"Updating discourse user %s groups: adding %s, removing %s" % (username, add_groups, rem_groups))
|
||||
"Updating discourse user %s groups: adding %s" % (username, add_groups))
|
||||
for g in add_groups:
|
||||
DiscourseManager.__add_user_to_group(g, username)
|
||||
if rem_groups:
|
||||
logger.info(
|
||||
"Updating discourse user %s groups: removing %s" % (username, rem_groups))
|
||||
for g in rem_groups:
|
||||
DiscourseManager.__remove_user_from_group(g, username)
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ class Teamspeak3Manager:
|
||||
group_cache = self.server.send_command('servergrouplist')
|
||||
logger.debug("Received group cache from server: %s" % group_cache)
|
||||
for group in group_cache:
|
||||
if group['keys']['type'] != '1':
|
||||
continue
|
||||
logger.debug("Checking group %s" % group)
|
||||
if group['keys']['name'] == groupname:
|
||||
logger.debug("Found group %s, returning id %s" % (groupname, group['keys']['sgid']))
|
||||
@@ -124,6 +126,8 @@ class Teamspeak3Manager:
|
||||
outlist = {}
|
||||
if group_cache:
|
||||
for group in group_cache:
|
||||
if group['keys']['type'] != '1':
|
||||
continue
|
||||
logger.debug("Assigning name/id dict: %s = %s" % (group['keys']['name'], group['keys']['sgid']))
|
||||
outlist[group['keys']['name']] = group['keys']['sgid']
|
||||
else:
|
||||
@@ -214,7 +218,7 @@ class Teamspeak3Manager:
|
||||
if isinstance(clients, dict):
|
||||
# Rewrap list
|
||||
clients = [clients]
|
||||
|
||||
|
||||
for client in clients:
|
||||
try:
|
||||
if client['keys']['client_database_id'] == user:
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
from allianceauth import NAME
|
||||
|
||||
from esi.clients import esi_client_factory
|
||||
import requests
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||
"""
|
||||
Swagger Operations:
|
||||
get_killmails_killmail_id_killmail_hash
|
||||
"""
|
||||
|
||||
|
||||
class SRPManager:
|
||||
@@ -18,7 +24,7 @@ class SRPManager:
|
||||
|
||||
@staticmethod
|
||||
def get_kill_data(kill_id):
|
||||
url = ("https://www.zkillboard.com/api/killID/%s/" % kill_id)
|
||||
url = ("https://zkillboard.com/api/killID/%s/" % kill_id)
|
||||
headers = {
|
||||
'User-Agent': NAME,
|
||||
'Content-Type': 'application/json',
|
||||
@@ -26,12 +32,20 @@ class SRPManager:
|
||||
r = requests.get(url, headers=headers)
|
||||
result = r.json()[0]
|
||||
if result:
|
||||
ship_type = result['victim']['ship_type_id']
|
||||
killmail_id = result['killmail_id']
|
||||
killmail_hash = result['zkb']['hash']
|
||||
c = esi_client_factory(spec_file=SWAGGER_SPEC_PATH)
|
||||
km = c.Killmails.get_killmails_killmail_id_killmail_hash(killmail_id=killmail_id,
|
||||
killmail_hash=killmail_hash).result()
|
||||
else:
|
||||
raise ValueError("Invalid Kill ID")
|
||||
if km:
|
||||
ship_type = km['victim']['ship_type_id']
|
||||
logger.debug("Ship type for kill ID %s is %s" % (kill_id, ship_type))
|
||||
ship_value = result['zkb']['totalValue']
|
||||
logger.debug("Total loss value for kill id %s is %s" % (kill_id, ship_value))
|
||||
victim_id = result['victim']['character_id']
|
||||
victim_id = km['victim']['character_id']
|
||||
return ship_type, ship_value, victim_id
|
||||
else:
|
||||
raise ValueError("Invalid Kill ID")
|
||||
raise ValueError("Invalid Kill ID or Hash.")
|
||||
|
||||
|
||||
1
allianceauth/srp/swagger.json
Normal file
1
allianceauth/srp/swagger.json
Normal file
File diff suppressed because one or more lines are too long
@@ -17,6 +17,8 @@
|
||||
<a href="{% url 'srp:all' %}" class="btn btn-primary">
|
||||
{% trans "View All" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.srp.add_srpfleetmain or perms.auth.srp_management %}
|
||||
<a href="{% url 'srp:add' %}" class="btn btn-success">
|
||||
{% trans "Add SRP Fleet" %}
|
||||
</a>
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models import Sum
|
||||
from allianceauth.authentication.decorators import permissions_required
|
||||
from allianceauth.eveonline.providers import provider
|
||||
from allianceauth.notifications import notify
|
||||
from .form import SrpFleetMainForm
|
||||
@@ -59,7 +60,7 @@ def srp_fleet_view(request, fleet_id):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.srp_management')
|
||||
@permissions_required(('auth.srp_management', 'srp.add_srpfleetmain'))
|
||||
def srp_fleet_add_view(request):
|
||||
logger.debug("srp_fleet_add_view called by user %s" % request.user)
|
||||
completed = False
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "Closed" %}</span>
|
||||
{% endif %}
|
||||
<a href="{{ notif.html_url }}" target="_blank">#{{ notif.number }} {{ notif.title }}</a>
|
||||
<a href="{{ notif.web_url }}" target="_blank">#{{ notif.number }} {{ notif.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="text-right" style="position:absolute;bottom:5px;right:5px;">
|
||||
<a href="https://github.com/allianceauth/allianceauth/issues"><span class="label label-default">
|
||||
<i class="fa fa-github" aria-hidden="true"></i> Powered by Github</span>
|
||||
<a href="https://gitlab.com/allianceauth/allianceauth/issues"><span class="label" style="background-color:#e65328;">
|
||||
<i class="fa fa-gitlab" aria-hidden="true"></i> Powered by GitLab</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
<li class="list-group-item list-group-item-{% if latest_major %}success{% else %}warning{% endif %}">
|
||||
<h4 class="list-group-item-heading">{% trans "Latest Major" %}</h4>
|
||||
<p class="list-group-item-text">
|
||||
<a href="{{ latest_major_url }}" style="color:#000"><i class="fa fa-github" aria-hidden="true"></i>
|
||||
<a href="https://gitlab.com/allianceauth/allianceauth/tags" style="color:#000"><i class="fa fa-gitlab" aria-hidden="true"></i>
|
||||
{{ latest_major_version }}
|
||||
</a>
|
||||
{% if not latest_major %}<br>{% trans "Update available" %}{% endif %}
|
||||
@@ -48,7 +48,7 @@
|
||||
<li class="list-group-item list-group-item-{% if latest_minor %}success{% else %}warning{% endif %}">
|
||||
<h4 class="list-group-item-heading">{% trans "Latest Minor" %}</h4>
|
||||
<p class="list-group-item-text">
|
||||
<a href="{{ latest_minor_url }}" style="color:#000"><i class="fa fa-github" aria-hidden="true"></i>
|
||||
<a href="https://gitlab.com/allianceauth/allianceauth/tags" style="color:#000"><i class="fa fa-gitlab" aria-hidden="true"></i>
|
||||
{{ latest_minor_version }}
|
||||
</a>
|
||||
{% if not latest_minor %}<br>{% trans "Update available" %}{% endif %}
|
||||
@@ -57,7 +57,7 @@
|
||||
<li class="list-group-item list-group-item-{% if latest_patch %}success{% else %}danger{% endif %}">
|
||||
<h4 class="list-group-item-heading">{% trans "Latest Patch" %}</h4>
|
||||
<p class="list-group-item-text">
|
||||
<a href="{{ latest_patch_url }}" style="color:#000"><i class="fa fa-github" aria-hidden="true"></i>
|
||||
<a href="https://gitlab.com/allianceauth/allianceauth/tags" style="color:#000"><i class="fa fa-gitlab" aria-hidden="true"></i>
|
||||
{{ latest_patch_version }}
|
||||
</a>
|
||||
{% if not latest_patch %}<br>{% trans "Update available" %}{% endif %}
|
||||
|
||||
@@ -17,16 +17,16 @@ NOTIFICATION_CACHE_TIME = 300 # 5 minutes
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_github_tags():
|
||||
request = requests.get('https://api.github.com/repos/allianceauth/allianceauth/releases')
|
||||
def get_git_tags():
|
||||
request = requests.get('https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/repository/tags')
|
||||
request.raise_for_status()
|
||||
return request.json()
|
||||
|
||||
|
||||
def get_github_notification_issues():
|
||||
def get_notification_issues():
|
||||
# notification
|
||||
request = requests.get(
|
||||
'https://api.github.com/repos/allianceauth/allianceauth/issues?labels=announcement&state=all')
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues?labels=announcement')
|
||||
request.raise_for_status()
|
||||
return request.json()
|
||||
|
||||
@@ -68,12 +68,12 @@ def get_notifications():
|
||||
'notifications': list(),
|
||||
}
|
||||
try:
|
||||
notifications = cache.get_or_set('github_notification_issues', get_github_notification_issues,
|
||||
notifications = cache.get_or_set('gitlab_notification_issues', get_notification_issues,
|
||||
NOTIFICATION_CACHE_TIME)
|
||||
# Limit notifications to those posted by repo owners and members
|
||||
response['notifications'] += [n for n in notifications if n['author_association'] in ['OWNER', 'MEMBER']][:5]
|
||||
response['notifications'] += notifications[:5]
|
||||
except requests.RequestException:
|
||||
logger.exception('Error while getting github notifications')
|
||||
logger.exception('Error while getting gitlab notifications')
|
||||
return response
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def get_version_info():
|
||||
'current_version': __version__,
|
||||
}
|
||||
try:
|
||||
tags = cache.get_or_set('github_release_tags', get_github_tags, TAG_CACHE_TIME)
|
||||
tags = cache.get_or_set('git_release_tags', get_git_tags, TAG_CACHE_TIME)
|
||||
current_ver = semver.Version.coerce(__version__)
|
||||
|
||||
# Set them all to the current version to start
|
||||
@@ -102,7 +102,7 @@ def get_version_info():
|
||||
})
|
||||
|
||||
for tag in tags:
|
||||
tag_name = tag.get('tag_name')
|
||||
tag_name = tag.get('name')
|
||||
if tag_name[0] == 'v':
|
||||
# Strip 'v' off front of verison if it exists
|
||||
tag_name = tag_name[1:]
|
||||
@@ -114,24 +114,21 @@ def get_version_info():
|
||||
if latest_major is None or tag_ver > latest_major:
|
||||
latest_major = tag_ver
|
||||
response['latest_major_version'] = tag_name
|
||||
response['latest_major_url'] = tag['html_url']
|
||||
if tag_ver.major > current_ver.major:
|
||||
response['latest_major'] = False
|
||||
elif tag_ver.major == current_ver.major:
|
||||
if latest_minor is None or tag_ver > latest_minor:
|
||||
latest_minor = tag_ver
|
||||
response['latest_minor_version'] = tag_name
|
||||
response['latest_minor_url'] = tag['html_url']
|
||||
if tag_ver.minor > current_ver.minor:
|
||||
response['latest_minor'] = False
|
||||
elif tag_ver.minor == current_ver.minor:
|
||||
if latest_patch is None or tag_ver > latest_patch:
|
||||
latest_patch = tag_ver
|
||||
response['latest_patch_version'] = tag_name
|
||||
response['latest_patch_url'] = tag['html_url']
|
||||
if tag_ver.patch > current_ver.patch:
|
||||
response['latest_patch'] = False
|
||||
|
||||
except requests.RequestException:
|
||||
logger.exception('Error while getting github release tags')
|
||||
logger.exception('Error while getting gitlab release tags')
|
||||
return response
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
```eval_rst
|
||||
.. tip::
|
||||
If you are uncomfortable with Linux permissions follow the steps below as the root user.
|
||||
If you are uncomfortable with Linux permissions follow the steps below as the root user.
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
@@ -12,7 +12,7 @@ Alliance Auth can be installed on any operating system. Dependencies are provide
|
||||
```eval_rst
|
||||
.. hint::
|
||||
CentOS: A few packages are included in a non-default repository. Add it and update the package lists. ::
|
||||
|
||||
|
||||
yum -y install https://centos7.iuscommunity.org/ius-release.rpm
|
||||
yum update
|
||||
```
|
||||
@@ -61,7 +61,7 @@ CentOS:
|
||||
```eval_rst
|
||||
.. important::
|
||||
CentOS: Make sure Redis is running before continuing. ::
|
||||
|
||||
|
||||
systemctl enable redis.service
|
||||
systemctl start redis.service
|
||||
```
|
||||
@@ -106,7 +106,7 @@ Create a Python virtual environment and put it somewhere convenient (e.g. `/home
|
||||
A virtual environment provides support for creating a lightweight "copy" of Python with their own site directories. Each virtual environment has its own Python binary (allowing creation of environments with various Python versions) and can have its own independent set of installed Python packages in its site directories. You can read more about virtual environments on the Python_ docs.
|
||||
.. _Python: https://docs.python.org/3/library/venv.html
|
||||
```
|
||||
|
||||
|
||||
Activate the virtualenv using `source /home/allianceserver/venv/auth/bin/activate`. Note the `/bin/activate` on the end of the path.
|
||||
|
||||
```eval_rst
|
||||
@@ -131,7 +131,7 @@ Django needs to install models to the database before it can start.
|
||||
python /home/allianceserver/myauth/manage.py migrate
|
||||
|
||||
Now we need to round up all the static files required to render templates. Make a directory to serve them from and populate it.
|
||||
|
||||
|
||||
mkdir -p /var/www/myauth/static
|
||||
python /home/allianceserver/myauth/manage.py collectstatic
|
||||
|
||||
@@ -182,7 +182,7 @@ You can check the status of the processes with `supervisorctl status`. Logs from
|
||||
```eval_rst
|
||||
.. note::
|
||||
Any time the code or your settings change you'll need to restart Gunicorn and Celery. ::
|
||||
|
||||
|
||||
supervisorctl restart myauth:
|
||||
```
|
||||
|
||||
@@ -202,10 +202,10 @@ If you intend to use this account as your personal auth account you need to add
|
||||
|
||||
## Updating
|
||||
|
||||
Periodically [new releases](https://github.com/allianceauth/allianceauth/releases/) are issued with bug fixes and new features. To update your install, simply activate your virtual environment and update with `pip install --upgrade allianceauth`. Be sure to read the release notes which will highlight changes.
|
||||
Periodically [new releases](https://gitlab.com/allianceauth/allianceauth/tags) are issued with bug fixes and new features. To update your install, simply activate your virtual environment and update with `pip install --upgrade allianceauth`. Be sure to read the release notes which will highlight changes.
|
||||
|
||||
Some releases come with changes to settings: update your project's settings with `allianceauth update /home/allianceserver/myauth`.
|
||||
|
||||
Some releases come with new or changed models. Update your database to reflect this with `python /home/allianceserver/myauth/manage.py migrate`.
|
||||
|
||||
|
||||
Always restart Celery and Gunicorn after updating.
|
||||
|
||||
@@ -56,7 +56,7 @@ Put comma-separated IDs into the brackets and the migration will create states w
|
||||
|
||||
If you used member/blue group names other than the standard "Member" and "Blue" you can enter settings to have the member/blue states created through this migration take these names.
|
||||
- `DEFAULT_AUTH_GROUP = ""` the desired name of the "Member" state
|
||||
- `DEFAULT_BLUE_GROUP = ""` the desired name of the "Blue" state
|
||||
- `DEFAULT_BLUE_GROUP = ""` the desired name of the "Blue" state
|
||||
|
||||
Any permissions assigned to these groups will be copied to the state replacing them. Because these groups are no longer managed they pose a security risk and so are deleted at the end of the migration automatically.
|
||||
|
||||
@@ -82,4 +82,4 @@ A similar process can be used to ensure users who may have lost service permissi
|
||||
|
||||
## Help
|
||||
|
||||
If something goes wrong during the migration reach out for help on [Gitter](https://gitter.im/R4stl1n/allianceauth) or open an [issue](https://github.com/allianceauth/allianceauth/issues).
|
||||
If something goes wrong during the migration reach out for help on [Gitter](https://gitter.im/R4stl1n/allianceauth) or open an [issue](https://gitlab.com/allianceauth/allianceauth/issues).
|
||||
|
||||
@@ -1,161 +1,7 @@
|
||||
# Alliance Market
|
||||
|
||||
## Dependencies
|
||||
Alliance Market requires PHP installed in your web server. Apache has `mod_php`, NGINX requires `php-fpm`.
|
||||
## Deprecation
|
||||
|
||||
## Prepare Your Settings
|
||||
In your auth project's settings file, do the following:
|
||||
- Add `'allianceauth.services.modules.market',` to your `INSTALLED_APPS` list
|
||||
- Append the following to the bottom of the settings file
|
||||
Alliance Market relies on the now non-functional XML API.
|
||||
|
||||
|
||||
# Alliance Market
|
||||
MARKET_URL = ''
|
||||
DATABASES['market'] = {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'alliance_market',
|
||||
'USER': 'allianceserver-market',
|
||||
'PASSWORD': 'password',
|
||||
'HOST': '127.0.0.1',
|
||||
'PORT': '3306',
|
||||
}
|
||||
|
||||
## Setup Alliance Market
|
||||
Alliance Market needs a database. Create one in MySQL/MariaDB. Default name is `alliance_market`:
|
||||
|
||||
mysql -u root -p
|
||||
create database alliance_market;
|
||||
grant all privileges on alliance_market . * to 'allianceserver'@'localhost';
|
||||
exit;
|
||||
|
||||
Install required packages to clone the repository:
|
||||
|
||||
apt-get install mercurial meld
|
||||
|
||||
Change to the web folder:
|
||||
|
||||
cd /var/www
|
||||
|
||||
Now clone the repository
|
||||
|
||||
hg clone https://bitbucket.org/krojew/evernus-alliance-market
|
||||
|
||||
Make cache and log directories
|
||||
|
||||
mkdir evernus-alliance-market/app/cache
|
||||
mkdir evernus-alliance-market/app/logs
|
||||
chmod -R 777 evernus-alliance-market/app/cache
|
||||
chmod -R 777 evernus-alliance-market/app/logs
|
||||
|
||||
Change ownership to apache
|
||||
|
||||
chown -R www-data:www-data evernus-alliance-market
|
||||
|
||||
Enter directory
|
||||
|
||||
cd evernus-alliance-market
|
||||
|
||||
Set environment variable
|
||||
|
||||
export SYMFONY_ENV=prod
|
||||
|
||||
Copy configuration
|
||||
|
||||
cp app/config/parameters.yml.dist app/config/parameters.yml
|
||||
|
||||
Edit, changing the following:
|
||||
- `database_name` to `alliance_market`
|
||||
- `database_user` to your MySQL user (usually `allianceserver`)
|
||||
- `database_password` to your MySQL user password
|
||||
- email settings, eg Gmail/Mailgun etc.
|
||||
|
||||
Edit `app/config/config.yml` and add the following:
|
||||
|
||||
services:
|
||||
fos_user.doctrine_registry:
|
||||
alias: doctrine
|
||||
|
||||
Install composer [as per these instructions.](https://getcomposer.org/download/)
|
||||
|
||||
Update dependencies.
|
||||
|
||||
php composer.phar update --optimize-autoloader
|
||||
|
||||
Prepare the cache:
|
||||
|
||||
php app/console cache:clear --env=prod --no-debug
|
||||
|
||||
|
||||
Dump assets:
|
||||
|
||||
php app/console assetic:dump --env=prod --no-debug
|
||||
|
||||
|
||||
Create DB entries
|
||||
|
||||
php app/console doctrine:schema:update --force
|
||||
|
||||
Install SDE:
|
||||
|
||||
php app/console evernus:update:sde
|
||||
|
||||
Configure your web server to serve alliance market.
|
||||
|
||||
A minimal Apache config might look like:
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName market.example.com
|
||||
DocumentRoot /var/www/evernus-alliance-market/web
|
||||
<Directory "/var/www/evernus-alliance-market/web/">
|
||||
DirectoryIndex app.php
|
||||
Require all granted
|
||||
AllowOverride all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
A minimal Nginx config might look like:
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name market.example.com;
|
||||
root /var/www/evernus-alliance-market/web;
|
||||
index app.php;
|
||||
access_log /var/logs/market.access.log;
|
||||
|
||||
# strip app.php/ prefix if it is present
|
||||
rewrite ^/app\.php/?(.*)$ /$1 permanent;
|
||||
|
||||
location / {
|
||||
index app.php;
|
||||
try_files $uri @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
rewrite ^(.*)$ /app.php/$1 last;
|
||||
}
|
||||
|
||||
# pass the PHP scripts to FastCGI server from upstream phpfcgi
|
||||
location ~ ^/(app|app_dev|config)\.php(/|$) {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param HTTPS off;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
Once again, set cache permissions:
|
||||
|
||||
chown -R www-data:www-data app/
|
||||
|
||||
Add a user account through auth, then make it a superuser:
|
||||
|
||||
php app/console fos:user:promote your_username --super
|
||||
|
||||
Now edit your auth project's settings file and fill in the web URL to your market as well as the database details.
|
||||
|
||||
Finally run migrations and restart Gunicorn and Celery.
|
||||
Please remove this service data with `python manage.py migrate appname zero` and then remove from your `INSTALLED_APPS` list.
|
||||
|
||||
@@ -22,7 +22,7 @@ Now two packages need to be installed:
|
||||
|
||||
apt-get install python-software-properties mumble-server
|
||||
|
||||
Download the appropriate authenticator release from [the authenticator repository](https://github.com/allianceauth/mumble-authenticator) and install the python dependencies for it:
|
||||
Download the appropriate authenticator release from [the authenticator repository](https://gitlab.com/allianceauth/mumble-authenticator) and install the python dependencies for it:
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ Nginx: `chown -R nginx:nginx /var/www/forums`
|
||||
|
||||
```eval_rst
|
||||
.. tip::
|
||||
Nginx: Some distributions use the ``www-data:www-data`` user:group instead of ``nginx:nginx``. If you run into problems with permissions try it instead.
|
||||
Nginx: Some distributions use the ``www-data:www-data`` user:group instead of ``nginx:nginx``. If you run into problems with permissions try it instead.
|
||||
..
|
||||
```
|
||||
|
||||
@@ -103,7 +103,7 @@ A minimal Nginx config file might look like:
|
||||
}
|
||||
}
|
||||
|
||||
Enter your forum's web address as the `PHPBB3_URL` setting in your auth project's settings file.
|
||||
Enter your forum's web address as the `PHPBB3_URL` setting in your auth project's settings file.
|
||||
|
||||
### Web Install
|
||||
Navigate to your forums web address where you will be presented with an installer.
|
||||
@@ -144,5 +144,15 @@ You can allow members to overwrite the portrait with a custom image if desired.
|
||||
|
||||

|
||||
|
||||
## Setting the default theme
|
||||
|
||||
Users generated via Alliance Auth do not have a default theme set. You will need to set this on the phpbb_users table in SQL
|
||||
|
||||
mysql -u root -p
|
||||
use alliance_forum;
|
||||
alter table phpbb_users change user_style user_style int not null default 1
|
||||
|
||||
If you would like to use a theme that is NOT prosilver or theme "1". You will need to deactivate prosilver, this will then fall over to the set forum wide default.
|
||||
|
||||
### Prepare Auth
|
||||
Once settings have been configured, run migrations and restart Gunicorn and Celery.
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
## Something broken? Stuck on an issue? Can't get it set up?
|
||||
|
||||
Start by checking the [issues](https://github.com/allianceauth/allianceauth/issues?q=is%3Aissue) - especially closed ones.
|
||||
Start by checking the [issues](https://gitlab.com/allianceauth/allianceauth/issues?scope=all&utf8=%E2%9C%93&state=all&search=my+issue) - especially closed ones.
|
||||
|
||||
No answer?
|
||||
- open an [issue](https://github.com/allianceauth/allianceauth/issues)
|
||||
- open an [issue](https://gitlab.com/allianceauth/allianceauth/issues)
|
||||
- harass us on [gitter](https://gitter.im/R4stl1n/allianceauth)
|
||||
|
||||
|
||||
## Logging
|
||||
|
||||
In its default configuration your auth project logs INFO and above messages to myauth/log/allianceauth.log. If you're encountering issues it's a good idea to view DEBUG messages as these greatly assist the troubleshooting process. These are printed to the console with manually starting the webserver via `python manage.py runserver`.
|
||||
|
||||
To record DEBUG messages in the log file, alter a setting in your auth project's settings file: `LOGGING['handlers']['log_file']['level'] = 'DEBUG'`. After restarting gunicorn and celery your log file will record all logging messages.
|
||||
To record DEBUG messages in the log file, alter a setting in your auth project's settings file: `LOGGING['handlers']['log_file']['level'] = 'DEBUG'`. After restarting gunicorn and celery your log file will record all logging messages.
|
||||
|
||||
## Common Problems
|
||||
|
||||
|
||||
8
setup.py
8
setup.py
@@ -12,16 +12,16 @@ install_requires = [
|
||||
'requests-oauthlib',
|
||||
'semantic_version',
|
||||
|
||||
'redis',
|
||||
'redis<=2.10.6',
|
||||
'celery>=4.0.2',
|
||||
'celery_once',
|
||||
|
||||
'django>=1.11',
|
||||
'django>=1.11,<=2.0.8',
|
||||
'django-bootstrap-form',
|
||||
'django-registration==2.4',
|
||||
'django-sortedm2m',
|
||||
'django-redis-cache>=1.7.1',
|
||||
'django-celery-beat',
|
||||
'django-celery-beat<=1.1.1',
|
||||
|
||||
'openfire-restapi',
|
||||
'sleekxmpp',
|
||||
@@ -50,7 +50,7 @@ setup(
|
||||
python_requires='~=3.4',
|
||||
license='GPLv2',
|
||||
packages=['allianceauth'],
|
||||
url='https://github.com/allianceauth/allianceauth',
|
||||
url='https://gitlab.com/allianceauth/allianceauth',
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
entry_points="""
|
||||
|
||||
4
tox.ini
4
tox.ini
@@ -17,4 +17,6 @@ deps=
|
||||
py37: https://github.com/yaml/pyyaml/zipball/master#egg=pyyaml
|
||||
py37: https://github.com/celery/kombu/zipball/master#egg=kombu
|
||||
install_command = pip install -e ".[testing]" -U {opts} {packages}
|
||||
commands=coverage run runtests.py -v 2
|
||||
commands =
|
||||
coverage run runtests.py -v 2
|
||||
coverage report -m
|
||||
|
||||
Reference in New Issue
Block a user