Compare commits

...

24 Commits

Author SHA1 Message Date
Adarnof
026b4c272b Correct startup error when settings blank 2017-01-22 12:15:32 -05:00
Adarnof
f5cb6a3fb7 Version bump to 1.14.2 2017-01-19 19:22:30 -05:00
Adarnof
0b57e027f7 Variable itemtype name datasources (#662)
Make provider settings optional
Wait to initialize adapter until first external call
Abstract get methods from adapter
2017-01-19 19:19:20 -05:00
Adarnof
3d50f977d9 Remove leftover IS_CORP references 2017-01-18 23:25:42 -05:00
Adarnof
0abb160886 Update links for new repo's new organization 2017-01-18 22:48:37 -05:00
Adarnof
ff3bb9743f Enforce unique IDs and names for eveonline models (#657)
Should help narrow down #656. Please report IntegrityErrors.
2017-01-18 19:52:04 -05:00
Adarnof
cf81f59fa6 Add missing titles to SRP buttons (#654)
Add pending request counter.
Restrict SRP status to choices.

Closes #643
2017-01-18 15:46:47 -05:00
Adarnof
d186088a8f Update universe types call to v2 format 2017-01-18 12:03:12 -05:00
Adarnof
2106e492e3 Adjust hierarchy for doc indexes 2017-01-18 00:46:09 -05:00
Adarnof
badc5a1f7f Include troubleshooting steps. 2017-01-18 00:43:13 -05:00
Adarnof
cced434a99 Correct API key form validation errors.
If an individual field fails, its value gets removed from the cleaned_data dict, but the full form clean() method still gets called.
As this form checks for api_id and api_key in the dict upon clean(), it would raise an unhandled KeyError
2017-01-17 23:49:55 -05:00
Adarnof
6c4e8ec2d1 Ensure correct corp and alliance groups are assigned.
Closes #650
2017-01-15 22:49:33 -05:00
ixof
5bbaef4476 Update Dashboard Image URL Scheme to HTTPS (#648) 2017-01-15 17:24:44 -05:00
Adarnof
6208071537 Include gcc-c++ CentOS dependency.
#645
2017-01-14 16:17:06 -05:00
Adarnof
df02982983 Login newly registered users.
Closes #642
2017-01-14 02:28:13 -05:00
Adarnof
eaa9d1930c Update documentation to allow removal of wiki pages. 2017-01-13 23:16:44 -05:00
Adarnof
24bc9d4b7f Provide feedback when ESI errors occur.
Closes #620
2017-01-13 21:56:27 -05:00
Adarnof
73641a7a1e Merge remote-tracking branch 'origin/master' 2017-01-13 21:33:18 -05:00
Adarnof
485c0fc373 Periodic task to update all corpstats every 6 hours.
Closes #617
2017-01-13 21:33:02 -05:00
Adarnof
55cbbadc2b Cache EVE API objects using django-redis to speed repeated queries. (#638) 2017-01-13 20:56:41 -05:00
Adarnof
b1dafeda8d Pathfinder setup instructions. 2017-01-13 20:53:58 -05:00
Adarnof
380069627a Correct Jacknife SQL step. 2017-01-13 19:17:50 -05:00
Adarnof
c3390b089e Include installation instructions for Jacknife
Update teamspeak binary links.
2017-01-13 19:13:07 -05:00
Adarnof
c9d9b0bf07 Install instructions fetch latest auth release 2017-01-12 21:11:04 -05:00
53 changed files with 861 additions and 299 deletions

View File

@@ -1,4 +1,4 @@
from __future__ import unicode_literals
__version__ = '1.14.1'
__version__ = '1.14.2'
NAME = 'Alliance Auth v%s' % __version__

View File

@@ -185,6 +185,16 @@ MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
#####################################################
##
## Auth configuration starts here
@@ -259,7 +269,6 @@ BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'Fals
# ENABLE_AUTH_IPS4 - Enable IPS4 support in the auth for auth'd members
# ENABLE_AUTH_SMF - Enable SMF forum support in the auth for auth'd members
# ENABLE_AUTH_MARKET = Enable Alliance Market support in auth for auth'd members
# ENABLE_AUTH_PATHFINDER = Enable Alliance Pathfinder suppor in auth for auth'd members
# ENABLE_AUTH_XENFORO = Enable XenForo forums support in the auth for auth'd members
#########################
ENABLE_AUTH_FORUM = 'True' == os.environ.get('AA_ENABLE_AUTH_FORUM', 'False')
@@ -286,7 +295,6 @@ ENABLE_AUTH_XENFORO = 'True' == os.environ.get('AA_ENABLE_AUTH_XENFORO', 'False'
# ENABLE_BLUE_IPS4 - Enable IPS4 forum support in the auth for blues
# ENABLE_BLUE_SMF - Enable SMF forum support in the auth for blues
# ENABLE_BLUE_MARKET - Enable Alliance Market in the auth for blues
# ENABLE_BLUE_PATHFINDER = Enable Pathfinder support in the auth for blues
# ENABLE_BLUE_XENFORO = Enable XenForo forum support in the auth for blue
#####################
ENABLE_BLUE_FORUM = 'True' == os.environ.get('AA_ENABLE_BLUE_FORUM', 'False')
@@ -351,12 +359,14 @@ API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False')
# EVEONLINE_CHARACTER_PROVIDER - Name of default data source for getting eve character data
# EVEONLINE_CORP_PROVIDER - Name of default data source for getting eve corporation data
# EVEONLINE_ALLIANCE_PROVIDER - Name of default data source for getting eve alliance data
# EVEONLINE_ITEMTYPE_PROVIDER - Name of default data source for getting eve item type data
#
# Available soruces are 'esi' and 'xml'
# Available sources are 'esi' and 'xml'. Leaving blank results in the default 'esi' being used.
#######################
EVEONLINE_CHARACTER_PROVIDER = os.environ.get('AA_EVEONLINE_CHARACTER_PROVIDER', 'esi')
EVEONLINE_CORP_PROVIDER = os.environ.get('AA_EVEONLINE_CORP_PROVIDER', 'esi')
EVEONLINE_ALLIANCE_PROVIDER = os.environ.get('AA_EVEONLINE_ALLIANCE_PROVIDER', 'esi')
EVEONLINE_CHARACTER_PROVIDER = os.environ.get('AA_EVEONLINE_CHARACTER_PROVIDER', '')
EVEONLINE_CORP_PROVIDER = os.environ.get('AA_EVEONLINE_CORP_PROVIDER', '')
EVEONLINE_ALLIANCE_PROVIDER = os.environ.get('AA_EVEONLINE_ALLIANCE_PROVIDER', '')
EVEONLINE_ITEMTYPE_PROVIDER = os.environ.get('AA_EVEONLINE_ITEMTYPE_PROVIDER', '')
#####################
# Alliance Market

View File

@@ -31,4 +31,4 @@ def post_save_user(sender, instance, created, *args, **kwargs):
# ensure all users have a model
if created:
AuthServicesInfo.objects.get_or_create(user=instance)

View File

@@ -110,6 +110,8 @@ def set_state(user):
auth.state = state
auth.save()
notify(user, "Membership State Change", message="You membership state has been changed to %s" % state)
assign_corp_group(auth)
assign_alliance_group(auth)
def assign_corp_group(auth):

View File

@@ -67,6 +67,7 @@ def register_user_view(request):
user.save()
logger.info("Created new user %s" % user)
login(request, user)
messages.warning(request, 'Add an API key to set up your account.')
return redirect("auth_dashboard")
@@ -107,7 +108,8 @@ def sso_login(request, token):
else:
messages.error(request, 'Your account has been disabled.')
else:
messages.warning(request, 'Authenticated character has no owning account. Please log in with username and password.')
messages.warning(request,
'Authenticated character has no owning account. Please log in with username and password.')
except EveCharacter.DoesNotExist:
messages.error(request, 'No account exists with the authenticated character. Please create an account first.')
return redirect(login_user)

View File

@@ -14,6 +14,7 @@ import logging
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class CorpStats(models.Model):
token = models.ForeignKey(Token, on_delete=models.CASCADE)
@@ -41,35 +42,42 @@ class CorpStats(models.Model):
def update(self):
try:
c = self.token.get_esi_client()
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()['corporation_id'] == int(self.corp.corporation_id)
members = c.Corporation.get_corporations_corporation_id_members(corporation_id=self.corp.corporation_id).result()
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
'corporation_id'] == int(self.corp.corporation_id)
members = c.Corporation.get_corporations_corporation_id_members(
corporation_id=self.corp.corporation_id).result()
member_ids = [m['character_id'] for m in members]
# requesting too many ids per call results in a HTTP400
# 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_id_chunks]
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_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['character_id']: m['character_name'] for m in name_chunk})
self.members = member_list
self.save()
except TokenError as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self, message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.", level="error")
notify(self.token.user, "%s failed to update with your ESI token." % self,
message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.",
level="error")
self.delete()
except HTTPForbidden as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self, message="%s: %s" % (e.status_code, e.message), level="error")
notify(self.token.user, "%s failed to update with your ESI token." % self,
message="%s: %s" % (e.status_code, e.message), level="error")
self.delete()
except AssertionError:
logger.warning("%s token character no longer in corp." % self)
if self.token.user:
notify(self.token.user, "%s cannot update with your ESI token." % self, message="%s cannot update with your ESI token as you have left corp." % self, level="error")
notify(self.token.user, "%s cannot update with your ESI token." % self,
message="%s cannot update with your ESI token as you have left corp." % self, level="error")
self.delete()
@property
@@ -95,7 +103,8 @@ class CorpStats(models.Model):
char = EveCharacter.objects.get(character_id=auth.main_char_id)
if char.corporation_id == self.corp.corporation_id and user.has_perm('corputils.corp_apis'):
return True
if self.corp.alliance and char.alliance_id == self.corp.alliance.alliance_id and user.has_perm('corputils.alliance_apis'):
if self.corp.alliance and char.alliance_id == self.corp.alliance.alliance_id and user.has_perm(
'corputils.alliance_apis'):
return True
if user.has_perm('corputils.blue_apis') and self.corp.is_blue:
return True
@@ -109,7 +118,6 @@ class CorpStats(models.Model):
def member_count(self):
return len(self.members)
@python_2_unicode_compatible
class MemberObject(object):
def __init__(self, character_id, character_name, show_apis=False):
@@ -144,12 +152,12 @@ class CorpStats(models.Model):
def get_member_objects(self, user):
show_apis = self.show_apis(user)
return sorted([CorpStats.MemberObject(id, name, show_apis=show_apis) for id, name in self.members.items()], key=attrgetter('character_name'))
return sorted([CorpStats.MemberObject(id, name, show_apis=show_apis) for id, name in self.members.items()],
key=attrgetter('character_name'))
def can_update(self, user):
return user.is_superuser or user == self.token.user
@python_2_unicode_compatible
class ViewModel(object):
def __init__(self, corpstats, user):

15
corputils/tasks.py Normal file
View File

@@ -0,0 +1,15 @@
from corputils.models import CorpStats
from celery.task import task, periodic_task
from celery.task.schedules import crontab
@task
def update_corpstats(pk):
cs = CorpStats.objects.get(pk=pk)
cs.update()
@periodic_task(run_every=crontab(minute=0, hour="*/6"))
def update_all_corpstats():
for cs in CorpStats.objects.all():
update_corpstats.delay(cs.pk)

View File

@@ -1,7 +1,7 @@
from django.conf.urls import url
import corputils.views
app_name='corputils'
app_name = 'corputils'
urlpatterns = [
url(r'^$', corputils.views.corpstats_view, name='view'),
url(r'^add/$', corputils.views.corpstats_add, name='add'),

View File

@@ -10,9 +10,11 @@ from django.conf import settings
from eveonline.models import EveCharacter, EveCorporationInfo
from corputils.models import CorpStats
from esi.decorators import token_required
from bravado.exception import HTTPError
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
def get_page(model_list, page_num):
p = Paginator(model_list, MEMBERS_PER_PAGE)
try:
@@ -23,8 +25,11 @@ def get_page(model_list, page_num):
members = p.page(p.num_pages)
return members
def access_corpstats_test(user):
return user.has_perm('corputils.view_corp_corpstats') or user.has_perm('corputils.view_alliance_corpstats') or user.has_perm('corputils.view_blue_corpstats')
return user.has_perm('corputils.view_corp_corpstats') or user.has_perm(
'corputils.view_alliance_corpstats') or user.has_perm('corputils.view_blue_corpstats')
@login_required
@user_passes_test(access_corpstats_test)
@@ -35,11 +40,13 @@ def corpstats_add(request, token):
if EveCharacter.objects.filter(character_id=token.character_id).exists():
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_id
else:
corp_id = token.get_esi_client().Character.get_characters_character_id(character_id=token.character_id).result()['corporation_id']
corp_id = \
token.get_esi_client().Character.get_characters_character_id(character_id=token.character_id).result()[
'corporation_id']
corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
cs = CorpStats.objects.create(token=token, corp=corp)
cs.update()
assert cs.pk # ensure update was succesful
assert cs.pk # ensure update was succesful
if CorpStats.objects.filter(pk=cs.pk).visible_to(request.user).exists():
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
except EveCorporationInfo.DoesNotExist:
@@ -50,11 +57,11 @@ def corpstats_add(request, token):
messages.error(request, 'Failed to gather corporation statistics with selected token.')
return redirect('corputils:view')
@login_required
@user_passes_test(access_corpstats_test)
def corpstats_view(request, corp_id=None):
corpstats = None
show_apis = False
# get requested model
if corp_id:
@@ -65,7 +72,7 @@ def corpstats_view(request, corp_id=None):
available = CorpStats.objects.visible_to(request.user)
# ensure we can see the requested model
if corpstats and not corpstats in available:
if corpstats and corpstats not in available:
raise PermissionDenied('You do not have permission to view the selected corporation statistics module.')
# get default model if none requested
@@ -84,22 +91,31 @@ def corpstats_view(request, corp_id=None):
if corpstats:
context.update({
'corpstats': corpstats.get_view_model(request.user),
'members': members,
'corpstats': corpstats.get_view_model(request.user),
'members': members,
})
return render(request, 'corputils/corpstats.html', context=context)
@login_required
@user_passes_test(access_corpstats_test)
def corpstats_update(request, corp_id):
corp = get_object_or_404(EveCorporationInfo, corporation_id=corp_id)
corpstats = get_object_or_404(CorpStats, corp=corp)
if corpstats.can_update(request.user):
corpstats.update()
try:
corpstats.update()
except HTTPError as e:
messages.error(request, str(e))
else:
raise PermissionDenied('You do not have permission to update member data for the selected corporation statistics module.')
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
raise PermissionDenied(
'You do not have permission to update member data for the selected corporation statistics module.')
if corpstats.pk:
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
else:
return redirect('corputils:view')
@login_required
@user_passes_test(access_corpstats_test)
@@ -109,9 +125,11 @@ def corpstats_search(request):
if search_string:
has_similar = CorpStats.objects.filter(_members__icontains=search_string).visible_to(request.user)
for corpstats in has_similar:
similar = [(member_id, corpstats.members[member_id]) for member_id in corpstats.members if search_string.lower() in corpstats.members[member_id].lower()]
similar = [(member_id, corpstats.members[member_id]) for member_id in corpstats.members if
search_string.lower() in corpstats.members[member_id].lower()]
for s in similar:
results.append((corpstats, CorpStats.MemberObject(s[0], s[1], show_apis=corpstats.show_apis(request.user))))
results.append(
(corpstats, CorpStats.MemberObject(s[0], s[1], show_apis=corpstats.show_apis(request.user))))
page = request.GET.get('page', 1)
results = sorted(results, key=lambda x: x[1].character_name)
results_page = get_page(results, page)

View File

@@ -3,6 +3,8 @@
AllianceAuth gets served using a Web Server Gateway Interface (WSGI) script. This script passes web requests to AllianceAuth which generates the content to be displayed and returns it. This means very little has to be configured in Apache to host AllianceAuth.
In the interest of ~~laziness~~ time-efficiency, scroll down for example configs. Use these, changing the ServerName to your domain name.
### Required Parameters for AllianceAuth Core
The AllianceAuth core requires the following parameters to be set:
@@ -58,12 +60,14 @@ You can supply your own SSL certificates if you so desire. The alternative is ru
- [000-default](http://pastebin.com/HfyKpQNu)
- [default-ssl](http://pastebin.com/2WCS5jnb)
### No SSL or Cloudflare
### No SSL Cloudflare, or LetsEncrypt
- Apache 2.4 or newer:
- [000-default.conf](http://pastebin.com/j1Ps3ZK6)
- Apache 2.3 or older:
- [000-default](http://pastebin.com/BHQzf2pj)
To have LetsEncrypt automatically install SSL certs, comment out the three lines starting with `WSGI`, install certificates, then uncomment them in `000-default-ls-ssl.conf`
## Enabling and Disabling Sites
To instruct apache to serve traffic from a virtual host, enable it:

View File

@@ -5,9 +5,10 @@ It's recommended to update all packages before proceeding.
`sudo yum upgrade`
`sudo reboot`
Now install all [dependencies](dependencies.md). For this guide you'll need the optional [JDK](dependencies.md) and [Apache](dependencies.md) sections as well.
Now install all [dependencies](dependencies.md).
sudo yum install xxxxxxx
replacing the x's with the list of packages.
Make sure redis is running before continuing:
@@ -28,9 +29,9 @@ Find the line which says `root ALL=(ALL) ALL` - beneath it add another
**From this point on you need to be logged in as the allianceserver user**
start your mariadb server `sudo systemctl start mariadb`
Start your mariadb server `sudo systemctl start mariadb`
secure your MYSQL / Maria-db server by typing `mysql_secure_installation `
Secure your MYSQL / Maria-db server by typing `mysql_secure_installation `
AllianceAuth needs a MySQL user account. Create one as follows, replacing `PASSWORD` with an actual secure password:
@@ -50,10 +51,14 @@ Ensure you are in the allianceserver home directory by issuing `cd`
Now we clone the source code:
git clone https://github.com/R4stl1n/allianceauth.git
git clone https://github.com/allianceauth/allianceauth.git
Enter the folder by issuing `cd allianceauth`
Ensure you're on the latest version with the following:
git tag | sort -n | tail -1 | xargs git checkout
Python package dependencies can be installed from the requirements file:
sudo pip install -r requirements.txt

View File

@@ -1,8 +1,8 @@
# CloudFlare
# Cloudflare
CloudFlare offers free SSL and DDOS mitigation services. Why not take advantage of it?
## Setup Protection
## Setup
Youll need to register an account on [CloudFlares site.](https://www.cloudflare.com/)
@@ -24,18 +24,12 @@ CloudFlare blocks ports outside 80 and 443 on hosts it protects. This means, if
## Redirect to HTTPS
Now we need to configure the https redirect to force all traffic to https. Along the top bar of CloudFlare, select `Page Rules`. Add a new rule, Pattern is yourdomain.com, toggle the `Always use https` to ON, and save. Itll take a few minutes to propagate.
Now we need to configure the https redirect to force all traffic to https. Along the top bar of CloudFlare, select `Page Rules`. Add a new rule, Pattern is yourdomain.com, toggle the `Always use https` to ON, and save. Itll take a few minutes to propagate.
![infographic](http://i.stack.imgur.com/VUBvo.jpg)
## Update Auth URLs
Edit settings.py and change the following values:
- FORUM_URL = `os.environ.get('AA_FORUM_URL', "http://forums.mydomain.com")` if forums are on a subdomain
- IPBOARD_ENDPOINT = `os.environ.get('AA_IPBOARD_ENDPOINT', 'http://forums.mydomain.com/ipboard/interface/board/index.php')` if forums are on a subdomain
- JABBER_URL = `os.environ.get('AA_JABBER_URL', "jabber.yourdomain.com")`
- OPENFIRE_ADDRESS = `os.environ.get('AA_OPENFIRE_ADDRESS', "http://jabber.yourdomain.com:9090")`
- MUMBLE_URL = `os.environ.get('AA_MUMBLE_URL', "mumble.yourdomain.com")`
- TEAMSPEAK3_PUBLIC_URL = `os.environ.get('AA_TEAMSPEAK3_PUBLIC_URL', 'ts.yourdomain.com')`
Edit settings.py and replace everything that has a HTTP with HTTPS (except anything with a port on the end, like `OPENFIRE_ADDRESS`)
And there we have it. Youre DDOS-protected with free SSL.
And there we have it. Youre DDOS-protected with free SSL.

View File

@@ -31,6 +31,7 @@ Required for phpBB, smf, evernus alliance market, etc
### Java
Required for hosting jabber server
oracle-java8-installer
## CentOS 7
@@ -53,7 +54,7 @@ Required for base auth site
#### Utilities
screen gcc unzip git redis curl nano
screen gcc gcc-c++ unzip git redis curl nano
### Apache
Required for displaying web content

View File

@@ -8,6 +8,7 @@
centos
settings
apache
cloudflare
supervisor
quickstart
```

View File

@@ -4,7 +4,7 @@ Once youve installed AllianceAuth, perform these steps to get yourself up and
First you need a superuser account. You can use this as a personal account. From the command line, `python manage.py createsuperuser` and follow the prompts.
The big goal of AllianceAuth is the automation of group membership, so well need some groups. In the admin interface, select `Groups`, then at the top-right select `Add Group`. Give it a name and select permissions. Special characters (including spaces) are removing before syncing to services, so try not to have group names which will be the same upon cleaning. A description of permissions can be found in the [readme file](https://github.com/R4stl1n/allianceauth/blob/master/README.md). Repeat for all the groups you see fit, whenever you need a new one.
The big goal of AllianceAuth is the automation of group membership, so well need some groups. In the admin interface, select `Groups`, then at the top-right select `Add Group`. Give it a name and select permissions. Special characters (including spaces) are removing before syncing to services, so try not to have group names which will be the same upon cleaning. A description of permissions can be found in the [readme file](https://github.com/allianceauth/allianceauth/blob/master/README.md). Repeat for all the groups you see fit, whenever you need a new one.
### Background Processes

View File

@@ -310,6 +310,8 @@ The default data source to get character information. Default is `esi`
The default data source to get corporation information. Default is `esi`
### EVEONLINE_ALLIANCE_PROVIDER
The default data source to get alliance information. Default is `esi`
### EVEONLINE_ITEMTYPE_PROVIDER
The default data source to get item type information. Default is `esi`
## Alliance Market
### MARKET_URL
The web address to access the Evernus Alliance Market application.

View File

@@ -40,13 +40,16 @@ Ensure you are in the allianceserver home directory by issuing `cd`
Now we clone the source code:
git clone https://github.com/R4stl1n/allianceauth.git
git clone https://github.com/allianceauth/allianceauth.git
Enter the folder by issuing `cd allianceauth`
Ensure you're on the latest version with the following:
git tag | sort -n | tail -1 | xargs git checkout
Python package dependencies can be installed from the requirements file:
sudo pip install requests>=2.9.1
sudo pip install -r requirements.txt
The settings file needs configuring. See [this lengthy guide](settings.md) for specifics.

View File

@@ -13,5 +13,7 @@
smf
teamspeak3
xenforo
jacknife
pathfinder
```

View File

@@ -1,16 +1,12 @@
# IPBoard3
Yes, you read that right. AllianceAuth only supports IPBoard 3, not the new shiny 4. Why? Because InvisionPower removed the API we used to manage it.
Moving right along.
Youre on your own for the initial install of IPBoard. Its pretty much just download, unzip, and move to `/var/www/ipboard/`. Make sure to
sudo chown -R www-data:www-data /var/www/ipboard
a few times because its pretty finicky.
Youll need to add another alias in your [apache config](https://github.com/R4stl1n/allianceauth/wiki/Apache-Setup#additional-parameters-for-full-setup), this one for `/ipboard/` pointing to `/var/www/ipboard` and add another `<directory>` block for `/var/www/ipboard` with `Require all granted` or `Allow from all` depending on your apache version.
Youll need to add another alias in your apache config, this one for `/ipboard` pointing to `/var/www/ipboard` and add another `<directory>` block for `/var/www/ipboard` with `Require all granted` or `Allow from all` depending on your apache version.
IPBoard needs a database table. Log in to mysql and run:
@@ -35,10 +31,10 @@ Copy the API key. Now edit your settings.py as follows:
- IPBOARD_APIKEY is the key you just copied
- IPBOARD_ENDPOINT is `http://yourdomain.com/ipboard/interface/board/index.php`
Now enable IPBoard for Auth and/or Blue by editing the [booleans](#alliance-service-setup).
Now enable IPBoard for Auth and/or Blue by editing the auth settings.
Save and exit. Restart apache or gunicorn.
Test it by creating a user through AllianceAuth. Just note right now theres no real error handling, so if account creation fails itll still return a username/password combo.
Test it by creating a user through Alliance Auth. Just note right now theres no real error handling, so if account creation fails itll still return a username/password combo.
Good luck!

View File

@@ -0,0 +1,71 @@
# Eve Jacknife
## Overview
Eve Jacknife is used to audit an api so that you might see character skills and what ships they can fly, mails, contracts,assets, and any other given access from a specific api key.
## Dependencies
All php and mysql dependencies should have been taken care of during setup.
## Installation
### Get Code
Navigate to your server's web directory: `cd /var/www`
Download the code: `sudo git clone https://github.com/whinis/eve-jacknife`
### Create Database
mysql -u root -p -e "create database jackknife; grant all privileges on jackknife.* to 'allianceserver'@'localhost';"
### Configure Settings
Change directory to jacknife: `cd eve-jacknife`
Now copy the template: `sudo cp base.config.php eve.config.php`
And now edit: `sudo nano eve.config.php`
Add the database user information:
- `$sql_u = "allianceserver"`
- `$sql_p = "MY_SQL_PASSWORD_HERE"`
## Apache Configuration
Change ownership of the directory: `sudo chown -R www-data:www-data ../eve-jacknife`
Eve Jacknife can be served two ways: on its own subdomain (`jacknife.mydomain.com`) or as an alias (`mydomain.com/jacknife`)
### Subdomain
As its own subdomain, create a new apache config: `sudo nano /etc/apache2/sites-available/jacknife.conf` and enter the following:
<VirtualHost *:80>
DocumentRoot "/var/www/eve-jacknife"
ServerName jacknife.mydomain.com
<Directory "/var/www/eve-jacknife">
Require all granted
AllowOverride all
DirectoryIndex index.php
</Directory>
</VirtualHost>
Enable the new site with `sudo a2ensite jacknife.conf` and then reload apache with `sudo service apache2 reload`
### Alias
As an alias, edit your site config (usually 000-default): `sudo nano etc/apache2/sites-available/000-default.conf` and add the following inside the `VirtualHost` block:
Alias /jacknife "/var/www/eve-jacknife/"
<Directory "/var/www/eve-jacknife">
Require all granted
DirectoryIndex index.php
</Directory>
Reload apache to take effect: `sudo service apache2 reload`
## Install SQL
Once apache is configured, Eve Jacknife needs to install some data. Navigate to it in your browser and append `/Installer.php` to the URL.
Enter your database password and press Check. If all the boxes come back green press Save. On the next screen press Install and wait for it to finish.
## Update Auth Settings
Edit your aut settings file (`nano ~/allianceauth/alliance_auth/settings.py`) and replace `JACK_KNIFE_URL` with either `jacknife.mydomain.com/` or `mydomain.com/jacknife/` depending on your apache choice.

View File

@@ -21,7 +21,7 @@ REQUIRED: To enable the ICE authenticator, edit the following:
- `icesecretwrite=MY_CLEVER_PASSWORD`, obviously choosing a secure password
To customize the database, edit the following:
By default mumble operates on sqlite which is fine, but slower than a dedicated MySQL server. To customize the database, edit the following:
- uncomment the database line, and change it to `database=alliance_mumble`
- `dbDriver=QMYSQL`
@@ -30,7 +30,7 @@ To customize the database, edit the following:
- `dbPort=3306`
- `dbPrefix=murmur_`
To name your root channel, uncomment and edit `registerName=` whatever cool name you want
To name your root channel, uncomment and set `registerName=` to whatever cool name you want
Save and close the file (control + O, control + X).
@@ -64,7 +64,7 @@ Edit `authenticator.ini` and change these values:
Test your configuration by starting it: `python authenticator.py`
#Running the Authenticator
## Running the Authenticator
The authenticator needs to be running 24/7 to validate users on Mumble. The best way is to run it in a screen much like celery:

View File

@@ -14,7 +14,7 @@ One additional package is required - [openjdk8](http://askubuntu.com/questions/4
### Download Installer
Openfire is not available through repositories so we need to get a debian from the developer.
On your PC, naviage to the [Ignite Realtime downloads section](https://www.igniterealtime.org/downloads/index.jsp), and under Openfire select Linux, click on the debian file (2nd in list, ends with .deb).
On your PC, naviage to the [Ignite Realtime downloads section](https://www.igniterealtime.org/downloads/index.jsp), and under Openfire select Linux, click on the debian file (2nd from bottom of list, ends with .deb).
Retrieve the file location by copying the url from the “click here” link.
@@ -22,11 +22,11 @@ In the console, ensure youre in your users home directory: `cd ~`
Now download the package. Replace the link below with the link you got earlier.
wget https://www.igniterealtime.org/downloadServlet?filename=openfire/openfire_3.10.2_all.deb
wget https://www.igniterealtime.org/downloadServlet?filename=openfire/openfire_4.1.1_all.deb
Now install from the debian. Replace the filename with your file name (the last part of the download url is the file name)
sudo dpkg -i openfire_3.10.2_all.deb
sudo dpkg -i openfire_4.1.1_all.deb
### Web Configuration
The remainder of the setup occurs through Openfires web interface. Navigate to http://yourdomain.com:9090, or if youre behind CloudFlare, go straight to your servers IP:9090.
@@ -58,7 +58,7 @@ Once loaded, press the green plus on the right for `REST API`.
Navigate the `Server` tab, `Sever Settings` subtab. At the bottom of the left navigation bar select `REST API`.
Select `Enabled`, and `Secret Key Auth`. Enter the secret key from OPENFIRE_SECRET_KEY here.
Select `Enabled`, and `Secret Key Auth`. Update Alliance Auth settings with this secret key as `OPENFIRE_SECRET_KEY`.
### Broadcast Plugin Setup

View File

@@ -0,0 +1,77 @@
# Pathfinder
Pathfinder is a wormhole mapping tool.
While auth doesn't integrate with pathfinder anymore, from personal experience I've found it much easier to use the following install process than to try and follow the pathfinder-supplied docs.
## Installation
### Get the code
Navigate to the install location: `cd /var/www/` and git clone the repo:
sudo git clone https://github.com/exodus4d/pathfinder.git
### Create logs and caches
Change directory to pathfinder: `cd pathfinder`
The logging and caching folders need to be created and have permission set. If upon installation you get Server Error 500, try resetting these permissions.
sudo mkdir logs
sudo mkdir tmp/cache
sudo chmod -R 766 logs
sudo chmod -R 766 tmp/cache
## .htaccess Configuration
In your `pathfinder` directory there are two `.htaccess` files. The default installation instructions want you to choose one for rewriting purposes, and these force you to www.pathfinder.mydomain.com. Personally I don't like that.
So we'll frankenstein our own. We'll use the HTTP one as a base:
sudo mv .htaccess .htaccess_HTTPS
sudo mv .htaccess_HTTPS .htaccess
sudo nano .htaccess
Find the www rewriting section (labelled `Rewrite NONE www. to force www.`). Change it so that all lines start with a `#`:
#RewriteCond %{HTTP_HOST} !^www\.
# skip "localhost" (dev environment)...
#RewriteCond %{HTTP_HOST} !=localhost
# skip IP calls (dev environment) e.g. 127.0.0.1
#RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
# rewrite everything else to "http://" and "www."
#RewriteRule .* http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
This allows us to choose SSL and www forwarding with our apache conf instead of this htaccess file.
## Apache Configuration
The best way to have this is to setup a subdomain on your server.
Create a config file `sudo nano /etc/apache2/sites-available/pathfinder.conf` and enter [this configuration](http://pastebin.com/wmXyf6pN), being sure to edit the `ServerName`
Enable it with:
sudo a2ensite pathfinder.conf
sudo service apache2 reload
## Configuration Files
The default configuration should be fine in most cases. Edit all values with caution!
environment.ini
- `SERVER` Should be changed to `PRODUCTION`
- `BASE` is the full filesystem path to the application root on your server. In our case, `/var/www/pathfinder/`
- `URL` Is the URL to your app (without a trailing slash). In our case, `http://pathfinder.mydomain.com`
- `DEBUG` sets the level of debugging (1,2 or 3) (check /logs for a more detail backtrace information)
- `DB_*` sets your DB connection information
- `SMTP_*` are used to send out emails, you will need an SMTP server login to make this work. (not required)
- `SSO_CCP_*` follow the [official docs](https://github.com/exodus4d/pathfinder/wiki/CREST)
## Database Setup
This is done through the browser. Go to `pathfinder.yourdomain.com/setup` and see the [official docs](https://github.com/exodus4d/pathfinder/wiki/Database) for instructions.
## Cron Jobs
Again the [official docs](https://github.com/exodus4d/pathfinder/wiki/Cronjob) do a good job here.
## Finish Setup
Once you've compelted the above steps, we need to disable the setup page. Edit the routes with `nano app/routes.ini` and put a `;` in front of the line starting with `GET @setup`

View File

@@ -14,11 +14,11 @@ In the console, navigate to your users home directory: `cd ~`
Now download using wget, replacing the url with the url for the package you just retrieved
wget https://www.phpbb.com/files/release/phpBB-3.1.6.zip
wget https://www.phpbb.com/files/release/phpBB-3.2.0.zip
This needs to be unpackaged. Unzip it, replacing the file name with that of the file you just downloaded
unzip phpBB-3.1.6.zip
unzip phpBB-3.2.0.zip
Now we need to move this to our web directory. Usually `/var/www/forums`.
@@ -47,7 +47,7 @@ You should see `Succesful Connection` and proceed.
Enter administrator credentials on the next page.
Everything from hereon out should be intuitive.
Everything from here should be intuitive.
phpBB will then write its own config file.
@@ -59,11 +59,11 @@ Before users can see the forums, we need to remove the install directory
### Enabling Avatars
AllianceAuth sets user avatars to their character portrait when the account is created or password reset. We need to allow external URLs for avatars for them to behave properly. Navigate to the admin control panel for phpbb3, and under the `General` tab, along the left navigation bar beneath `Board Configuration`, select `Avatar Settings`. Set `Enable Remote Avatars` to `Yes` and then `Submit`.
[Screenshot of this page](http://imgur.com/UOgaq6J)
![location of the remote avatar setting](http://i.imgur.com/eWrotRX.png)
You can allow members to overwrite the portrait with a custom image if desired. Navigate to `Users and Groups`, `Group Permissions`, select the appropriate group (usually `Member` if you want everyone to have this ability), expand `Advanced Permissions`, under the `Profile` tab, set `Can Change Avatars` to `Yes`, and press `Apply Permissions`.
[Screenshot of this page](http://i.imgur.com/VGHwdxM.png)
![location of change avatar setting](http://i.imgur.com/Nc6Rzo9.png)
## Setup Complete
Youve finished the steps required to make AllianceAuth work with phpBB. Play around with it and make it your own.

View File

@@ -14,11 +14,11 @@ In the console, navigate to your users home directory: `cd ~`
Now download using wget, replacing the url with the url for the package you just retrieved
wget http://download.simplemachines.org/index.php?thanks;filename=smf_2-0-11_install.zip
wget http://download.simplemachines.org/index.php?thanks;filename=smf_2-0-13_install.zip
This needs to be unpackaged. Unzip it, replacing the file name with that of the file you just downloaded
unzip smf_2-0-11_install.zip
unzip smf_2-0-13_install.zip
Now we need to move this to our web directory. Usually `/var/www/forums`.

View File

@@ -3,10 +3,14 @@
## Overview
Teamspeak3 is the most popular VOIP program for gamers.
But have you considered using Mumble? Not only is it free, but it has features and performance far superior to Teamspeak3.
## Dependencies
All dependencies should have been taken care of during the AllianceAuth install.
## Setup
Sticking with it? Alright, I tried.
### Download Installer
To install we need a copy of the server. You can find the latest version from [this dl server](http://dl.4players.de/ts/releases/) (Id recommed getting the latest stable version find this version number from the [TeamSpeak site](https://www.teamspeak.com/downloads#)). Be sure to get a link to the linux version.
@@ -14,11 +18,11 @@ From the console, ensure youre in the users home directory: `cd ~`
And now download the server, replacing the link with the link you got earlier.
wget http://dl.4players.de/ts/releases/3.0.11.4/teamspeak3-server_linux-amd64-3.0.11.4.tar.gz
http://dl.4players.de/ts/releases/3.0.13.6/teamspeak3-server_linux_amd64-3.0.13.6.tar.bz2
Now we need to extract the file.
tar -xvf teamspeak3-server_linux-amd64-3.0.11.4.tar.gz
tar -xf teamspeak3-server_linux_amd64-3.0.13.6.tar.bz2
### Create User
Teamspeak needs its own user.
@@ -28,15 +32,13 @@ Teamspeak needs its own user.
### Install Binary
Now we move the server binary somewhere more accessible and change its ownership to the new user.
sudo mv teamspeak3-server_linux-amd64 /usr/local/teamspeak
sudo mv teamspeak3-server_linux_amd64 /usr/local/teamspeak
sudo chown -R teamspeak:teamspeak /usr/local/teamspeak
### Startup
Now we generate a startup script so teamspeak comes up with the server.
sudo ln -s /usr/local/teamspeak/ts3server_startscript.sh /etc/init.d/teamspeak
sudo update-rc.d teamspeak defaults
Finally we start the server.
@@ -46,14 +48,16 @@ Finally we start the server.
### Update Settings
The console will spit out a block of text. **SAVE THIS**.
Update the AllianceAuth settings file with the following:
- TEAMSPEAK3_SERVERQUERY_USER is `loginname`
- TEAMSPEAK3_SERVERQUERY_PASSWORD is `password`
Update the AllianceAuth settings file with the following from that block of text:
- `TEAMSPEAK3_SERVERQUERY_USER` is `loginname` (usually `serveradmin`)
- `TEAMSPEAK3_SERVERQUERY_PASSWORD` is `password`
Save and reload apache.
Save and reload apache. Restart celery workers as well.
sudo service apache2 reload
If you plan on claiming the ServerAdmin token, do so with a different TeamSpeak client profile than the one used for your auth account, or you will lose your admin status.
### Generate User Account
And now we can generate ourselves a user account. Navigate to the services in AllianceAuth for your user account and press the checkmark for TeamSpeak 3.
@@ -65,6 +69,47 @@ Now we need to make groups. AllianceAuth handles groups in teamspeak differently
Navigate back to the AllianceAuth admin interface (yourdomain.com/admin) and under `Services`, select `Auth / TS Groups`. In the top-right corner click `Add`.
The dropdown box provides all auth groups. Select one and assign TeamSpeak groups from the panels below. If these panels are empty, wait a minute for the database update to run.
The dropdown box provides all auth groups. Select one and assign TeamSpeak groups from the panels below. If these panels are empty, wait a minute for the database update to run, or see the [troubleshooting section](#ts-group-models-not-populating-on-admin-site) below.
## Setup Complete
## Troubleshooting
### `Insufficient client permissions (failed on Invalid permission: 0x26)`
Using the advanced permissions editor, ensure the `Guest` group has the permission `Use Privilege Keys to gain permissions` (under `Virtual Server` expand the `Administration` section)
To enable advanced permissions, on your client go to the `Tools` menu, `Application`, and under the `Misc` section, tick `Advanced permission system`
### TS group models not populating on admin site
The method which populates these runs every 30 minutes. To populate manually, start a celery shell:
python manage.py celery shell
And execute the update:
run_ts3_group_update()
Ensure that command does not return an error.
### `2564 access to default group is forbidden`
This usually occurs because auth is trying to remove a user from the `Guest` group (group ID 8). The guest group is only assigned to a user when they have no other groups, unless you have changed the default teamspeak server config.
Teamspeak servers v3.0.13 and up are especially susceptible to this. Ensure the Channel Admin Group is not set to `Guest (8)`. Check by right clicking on the server name, `Edit virtual server`, and in the middle of the panel select the `Misc` tab.
### `TypeError: string indices must be integers, not str`
This error generally means teamspeak returned an error message that went unhandled. The full traceback is required for proper debugging, which the logs do not record. Please check the superuser notifications for this record and get in touch with a developer.
### `3331 flood ban`
This most commonly happens when your teamspeak server is externally hosted. You need to add the auth server IP to the teamspeak serverquery whitelist. This varies by provider.
If you have SSH access to the server hosting it, you need to locate the teamspeak server folder and add the auth server IP on a new line in `server_query_whitelist.txt`
### `520 invalid loginname or password`
The serverquery account login specified in settings.py is incorrect. Please verify `TEAMSPEAK3_SERVERQUERY_USER` and `TEAMSPEAK3_SERVERQUERY_PASSWORD`. The [installation section](#update-settings) describes where to get them.
### `2568 insufficient client permissions`
This usually occurs if you've created a separate serverquery user to use with auth. It has not been assigned sufficient permissions to complete all the tasks required of it. The full list of required permissions is not known, so assign liberally.

View File

@@ -1,12 +1,12 @@
# Changelog
## From now on all changelogs will be included as release notes.
https://github.com/R4stl1n/allianceauth/releases
https://github.com/allianceauth/allianceauth/releases
### 547
Oct 16
Golly this is a big one. Upgrading takes a bit of work. [For full instructions click here.](https://github.com/R4stl1n/allianceauth/pull/547#issue-183247630)
Golly this is a big one. Upgrading takes a bit of work. [For full instructions click here.](https://github.com/allianceauth/allianceauth/pull/547#issue-183247630)
- Update django version to 1.10
- Remove member/blue permissions
@@ -100,7 +100,7 @@ Mar 23 2016
### 314
Mar 22 2016
- Revamp of the Human Resources Application and Management System
- see the [wiki page](https://github.com/R4stl1n/allianceauth/wiki/HRApplications) for how to use the new system
- see the [docs](../features/hrapplications.md) for how to use the new system
- a completely untested conversion script exists. If you want to view your old models, contact Adarnof to try it out
- Moved Error Handling for the API Keys to the API Calls to better handle API server outages
- Removed the infamous database update task

View File

@@ -6,6 +6,5 @@
changelog
troubleshooting
cloudflare
```

View File

@@ -3,12 +3,11 @@
## Something broken? Stuck on an issue? Can't get it set up?
Start here:
- read the [documentation](https://github.com/R4stl1n/allianceauth/wiki)
- check the [issues](https://github.com/R4stl1n/allianceauth/issues?utf8=%E2%9C%93&q=is%3Aissue) - especially closed ones
- check the [issues](https://github.com/allianceauth/allianceauth/issues?utf8=%E2%9C%93&q=is%3Aissue) - especially closed ones
- check the [forums](https://forums.eveonline.com/default.aspx?g=posts&t=383030)
No answer?
- open an [issue](https://github.com/R4stl1n/allianceauth/issues)
- open an [issue](https://github.com/allianceauth/allianceauth/issues)
- harass us on [gitter](https://gitter.im/R4stl1n/allianceauth)
- post to the [forums](https://forums.eveonline.com/default.aspx?g=posts&t=383030)
@@ -16,9 +15,7 @@ No answer?
### `pip install -r requirements.txt` is failing
Most commonly, your repositories did not include the `requests` package. Install it and try again: `sudo pip install requests`
Otherwise it's usually a missing dependency. Check [the list](../installation/auth/dependencies.md), reinstall, and try again.
Either you need to `sudo` that command, or it's a missing dependency. Check [the list](../installation/auth/dependencies.md), reinstall, and try again.
### I'm getting an error 500 trying to connect to the website on a new install
@@ -26,7 +23,11 @@ Read the apache error log: `sudo nano /var/log/apache2/error.log`
If it talks about failing to import something, google its name and install it.
If it whines about being unable to configure logger, make sure the log directory is write-able: `chmod -R 777 /home/allianceserver/allianceauth/log`, then reload apache.
If it whines about being unable to configure logger, see below.
### Failed to configure log handler
Make sure the log directory is write-able: `chmod -R 777 /home/allianceserver/allianceauth/log`, then reload apache/celery/supervisor/etc.
### Groups aren't syncing to services
@@ -34,9 +35,7 @@ Make sure the background processes are running: `ps aux | grep celery` should re
If that doesn't do it, try clearing the worker queue. First kill all celery processes as described above, then do the following:
sudo rabbitmqctl stop_app
sudo rabbitmqctl reset
sudo rabbitmqctl start_app
redis-cli FLUSHALL
python manage.py celeryd --purge
Press control+C once.

View File

@@ -13,7 +13,17 @@ admin.site.register(EveCorporationInfo)
class EveApiKeyPairAdmin(admin.ModelAdmin):
search_fields = ['api_id', 'user__username']
list_display = ['api_id', 'user']
list_display = ['api_id', 'user', 'characters']
@staticmethod
def characters(obj):
return ', '.join(sorted([c.character_name for c in EveCharacter.objects.filter(api_id=obj.api_id)]))
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super(EveApiKeyPairAdmin, self).get_search_results(request, queryset, search_term)
chars = EveCharacter.objects.filter(character_name__icontains=search_term)
queryset |= EveApiKeyPair.objects.filter(api_id__in=[char.api_id for char in chars if bool(char.api_id)])
return queryset, use_distinct
class EveCharacterAdmin(admin.ModelAdmin):

View File

@@ -28,7 +28,10 @@ class UpdateKeyForm(forms.Form):
raise forms.ValidationError("API ID must be a number")
def clean(self):
super(UpdateKeyForm, self).clean()
if 'api_id' not in self.cleaned_data or 'api_key' not in self.cleaned_data:
# need to check if api_id and vcode in cleaned_data because
# if they fail, they get removed from the dict but this method still happens
return self.cleaned_data
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'])

View File

@@ -9,15 +9,23 @@ import logging
logger = logging.getLogger(__name__)
adapter = eve_adapter_factory()
class EveManager:
def __init__(self):
pass
class EveManager(object):
adapter = None
@classmethod
def get_adapter(cls):
if not cls.adapter:
cls.adapter = eve_adapter_factory()
return cls.adapter
@classmethod
def get_character(cls, character_id):
return cls.get_adapter().get_character(character_id)
@staticmethod
def create_character(id, user, api_id):
return EveManager.create_character_obj(adapter.get_character(id), user, api_id)
return EveManager.create_character_obj(EveManager.get_character(id), user, api_id)
@staticmethod
def create_character_obj(character, user, api_id):
@@ -35,7 +43,7 @@ class EveManager:
@staticmethod
def update_character(id):
return EveManager.update_character_obj(adapter.get_character(id))
return EveManager.update_character_obj(EveManager.get_character(id))
@staticmethod
def update_character_obj(char):
@@ -61,9 +69,13 @@ class EveManager:
else:
logger.warn("Attempting to create existing api keypair with id %s" % api_id)
@classmethod
def get_alliance(cls, alliance_id):
return cls.get_adapter().get_alliance(alliance_id)
@staticmethod
def create_alliance(id, is_blue=False):
return EveManager.create_alliance_obj(adapter.get_alliance(id), is_blue=is_blue)
return EveManager.create_alliance_obj(EveManager.get_alliance(id), is_blue=is_blue)
@staticmethod
def create_alliance_obj(alliance, is_blue=False):
@@ -77,7 +89,7 @@ class EveManager:
@staticmethod
def update_alliance(id, is_blue=None):
return EveManager.update_alliance_obj(adapter.get_alliance(id), is_blue=is_blue)
return EveManager.update_alliance_obj(EveManager.get_alliance(id), is_blue=is_blue)
@staticmethod
def update_alliance_obj(alliance, is_blue=None):
@@ -89,17 +101,20 @@ class EveManager:
@staticmethod
def populate_alliance(id):
alliance_model = EveAllianceInfo.objects.get(alliance_id=id)
alliance = adapter.get_alliance(id)
alliance = EveManager.get_alliance(id)
for corp_id in alliance.corp_ids:
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
EveManager.create_corporation(corp_id, is_blue=alliance_model.is_blue)
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(alliance=alliance_model)
EveCorporationInfo.objects.filter(alliance=alliance_model).exclude(corporation_id__in=alliance.corp_ids).update(alliance=None)
@classmethod
def get_corporation(cls, corp_id):
return cls.get_adapter().get_corp(corp_id)
@staticmethod
def create_corporation(id, is_blue=False):
return EveManager.create_corporation_obj(adapter.get_corp(id), is_blue=is_blue)
return EveManager.create_corporation_obj(EveManager.get_corporation(id), is_blue=is_blue)
@staticmethod
def create_corporation_obj(corp, is_blue=False):
@@ -118,7 +133,7 @@ class EveManager:
@staticmethod
def update_corporation(id, is_blue=None):
return EveManager.update_corporation_obj(adapter.get_corp(id), is_blue=is_blue)
return EveManager.update_corporation_obj(EveManager.get_corporation(id), is_blue=is_blue)
@staticmethod
def update_corporation_obj(corp, is_blue=None):
@@ -131,10 +146,14 @@ class EveManager:
model.is_blue = model.is_blue if is_blue == None else is_blue
model.save()
@classmethod
def get_itemtype(cls, type_id):
return cls.get_adapter().get_itemtype(type_id)
@staticmethod
def get_characters_from_api(api):
char_result = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
provider = EveXmlProvider(adapter=adapter)
provider = EveXmlProvider(adapter=EveManager.get_adapter())
return [provider._build_character(result) for id, result in char_result.items()]
@staticmethod

View File

@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-01-18 13:20
from __future__ import unicode_literals
from django.db import migrations, models
def get_duplicates(items):
return set([item for item in items if items.count(item) > 1])
def enforce_unique_characters(apps, schema_editor):
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
ids = [c.character_id for c in EveCharacter.objects.all()]
duplicates = get_duplicates(ids)
for c_id in duplicates:
dupes = EveCharacter.objects.filter(character_id=c_id)
dupes.exclude(pk=dupes[0].pk).delete()
names = [c.character_name for c in EveCharacter.objects.all()]
duplicates = get_duplicates(names)
for name in duplicates:
dupes = EveCharacter.objects.filter(character_name=name)
dupes.exclude(pk=dupes[0].pk).delete()
def enforce_unique_corporations(apps, schema_editor):
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
ids = [c.corporation_id for c in EveCorporationInfo.objects.all()]
duplicates = get_duplicates(ids)
for c_id in duplicates:
dupes = EveCorporationInfo.objects.filter(corporation_id=c_id)
dupes.exclude(pk=dupes[0].pk).delete()
names = [c.corporation_name for c in EveCorporationInfo.objects.all()]
duplicates = get_duplicates(names)
for name in duplicates:
dupes = EveCorporationInfo.objects.filter(character_name=name)
dupes.exclude(pk=dupes[0].pk).delete()
def enforce_unique_alliances(apps, schema_editor):
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
ids = [a.alliance_id for a in EveAllianceInfo.objects.all()]
duplicates = get_duplicates(ids)
for a_id in duplicates:
dupes = EveAllianceInfo.objects.filter(alliance_id=a_id)
to_be_kept = dupes[0]
EveCorporationInfo.objects.filter(alliance__pk__in=[a.pk for a in dupes.exclude(pk=to_be_kept.pk)]).update(
alliance=to_be_kept.pk)
dupes.exclude(pk=to_be_kept.pk).delete()
names = [a.alliance_name for a in EveAllianceInfo.objects.all()]
duplicates = get_duplicates(names)
for name in duplicates:
dupes = EveAllianceInfo.objects.filter(alliance_name=name)
to_be_kept = dupes[0]
EveCorporationInfo.objects.filter(alliance__in=[a.pk for a in dupes.exclude(pk=to_be_kept.pk)]).update(
alliance=to_be_kept.pk)
dupes.exclude(pk=to_be_kept.pk).delete()
def enforce_unique_apis(apps, schema_editor):
EveApiKeyPair = apps.get_model('eveonline', 'EveApiKeyPair')
ids = [api.api_id for api in EveApiKeyPair.objects.all()]
duplicates = get_duplicates(ids)
for api_id in duplicates:
dupes = EveApiKeyPair.objects.filter(api_id=api_id)
dupes.exclude(pk=dupes[0].pk).delete()
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0006_allow_null_evecharacter_alliance'),
]
operations = [
migrations.RunPython(enforce_unique_characters, migrations.RunPython.noop),
migrations.RunPython(enforce_unique_corporations, migrations.RunPython.noop),
migrations.RunPython(enforce_unique_alliances, migrations.RunPython.noop),
migrations.RunPython(enforce_unique_apis, migrations.RunPython.noop),
migrations.AlterField(
model_name='evecharacter',
name='character_id',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='evecharacter',
name='character_name',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='evecorporationinfo',
name='corporation_id',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='evecorporationinfo',
name='corporation_name',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='eveallianceinfo',
name='alliance_id',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='eveallianceinfo',
name='alliance_name',
field=models.CharField(max_length=254, unique=True),
),
migrations.AlterField(
model_name='eveapikeypair',
name='api_id',
field=models.CharField(max_length=254, unique=True),
),
]

View File

@@ -6,8 +6,8 @@ from django.contrib.auth.models import User
@python_2_unicode_compatible
class EveCharacter(models.Model):
character_id = models.CharField(max_length=254)
character_name = models.CharField(max_length=254)
character_id = models.CharField(max_length=254, unique=True)
character_name = models.CharField(max_length=254, unique=True)
corporation_id = models.CharField(max_length=254)
corporation_name = models.CharField(max_length=254)
corporation_ticker = models.CharField(max_length=254)
@@ -22,7 +22,7 @@ class EveCharacter(models.Model):
@python_2_unicode_compatible
class EveApiKeyPair(models.Model):
api_id = models.CharField(max_length=254)
api_id = models.CharField(max_length=254, unique=True)
api_key = models.CharField(max_length=254)
user = models.ForeignKey(User, blank=True, null=True)
sso_verified = models.BooleanField(default=False)
@@ -33,8 +33,8 @@ class EveApiKeyPair(models.Model):
@python_2_unicode_compatible
class EveAllianceInfo(models.Model):
alliance_id = models.CharField(max_length=254)
alliance_name = models.CharField(max_length=254)
alliance_id = models.CharField(max_length=254, unique=True)
alliance_name = models.CharField(max_length=254, unique=True)
alliance_ticker = models.CharField(max_length=254)
executor_corp_id = models.CharField(max_length=254)
is_blue = models.BooleanField(default=False)
@@ -45,8 +45,8 @@ class EveAllianceInfo(models.Model):
@python_2_unicode_compatible
class EveCorporationInfo(models.Model):
corporation_id = models.CharField(max_length=254)
corporation_name = models.CharField(max_length=254)
corporation_id = models.CharField(max_length=254, unique=True)
corporation_name = models.CharField(max_length=254, unique=True)
corporation_ticker = models.CharField(max_length=254)
member_count = models.IntegerField()
is_blue = models.BooleanField(default=False)

View File

@@ -1,14 +1,23 @@
from django.utils.encoding import python_2_unicode_compatible
from esi.clients import esi_client_factory
from django.conf import settings
from django.core.cache import cache
import json
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
import evelink
import logging
logger = logging.getLogger(__name__)
# optional setting to control cached object lifespan
OBJ_CACHE_DURATION = int(getattr(settings, 'EVEONLINE_OBJ_CACHE_DURATION', 300))
@python_2_unicode_compatible
class ObjectNotFound(Exception):
def __init__(self, id, type):
self.id = id
self.type = type
def __init__(self, obj_id, type_name):
self.id = obj_id
self.type = type_name
def __str__(self):
return '%s with ID %s not found.' % (self.type, self.id)
@@ -32,6 +41,16 @@ class Entity(object):
def __eq__(self, other):
return self.id == other.id
def serialize(self):
return {
'id': self.id,
'name': self.name,
}
@classmethod
def from_dict(cls, data_dict):
return cls(data_dict['id'], data_dict['name'])
class Corporation(Entity):
def __init__(self, provider, id, name, ticker, ceo_id, members, alliance_id):
@@ -58,6 +77,28 @@ class Corporation(Entity):
self._ceo = self.provider.get_character(self.ceo_id)
return self._ceo
def serialize(self):
return {
'id': self.id,
'name': self.name,
'ticker': self.ticker,
'ceo_id': self.ceo_id,
'members': self.members,
'alliance_id': self.alliance_id
}
@classmethod
def from_dict(cls, dict):
return cls(
None,
dict['id'],
dict['name'],
dict['ticker'],
dict['ceo_id'],
dict['members'],
dict['alliance_id'],
)
class Alliance(Entity):
def __init__(self, provider, id, name, ticker, corp_ids, executor_corp_id):
@@ -70,7 +111,7 @@ class Alliance(Entity):
def corp(self, id):
assert id in self.corp_ids
if not id in self._corps:
if id not in self._corps:
self._corps[id] = self.provider.get_corp(id)
self._corps[id]._alliance = self
return self._corps[id]
@@ -83,6 +124,26 @@ class Alliance(Entity):
def executor_corp(self):
return self.corp(self.executor_corp_id)
def serialize(self):
return {
'id': self.id,
'name': self.name,
'ticker': self.ticker,
'corp_ids': self.corp_ids,
'executor_corp_id': self.executor_corp_id,
}
@classmethod
def from_dict(cls, dict):
return cls(
None,
dict['id'],
dict['name'],
dict['ticker'],
dict['corp_ids'],
dict['executor_corp_id'],
)
class Character(Entity):
def __init__(self, provider, id, name, corp_id, alliance_id):
@@ -96,7 +157,7 @@ class Character(Entity):
@property
def corp(self):
if not self._corp:
self._corp = self.provider.get_corp(self.corp_id)
self._corp = self.provider.get_corp(self.corp_id)
return self._corp
@property
@@ -105,8 +166,40 @@ class Character(Entity):
return self.corp.alliance
return Entity(None, None)
def serialize(self):
return {
'id': self.id,
'name': self.name,
'corp_id': self.corp_id,
'alliance_id': self.alliance_id,
}
class EveProvider:
@classmethod
def from_dict(cls, dict):
return cls(
None,
dict['id'],
dict['name'],
dict['corp_id'],
dict['alliance_id'],
)
class ItemType(Entity):
def __init__(self, provider, type_id, name):
super(ItemType, self).__init__(type_id, name)
self.provider = provider
@classmethod
def from_dict(cls, data_dict):
return cls(
None,
data_dict['id'],
data_dict['name'],
)
class EveProvider(object):
def get_alliance(self, alliance_id):
"""
:return: an Alliance object for the given ID
@@ -119,12 +212,18 @@ class EveProvider:
"""
raise NotImplementedError()
def get_character(self, corp_id):
def get_character(self, character_id):
"""
:return: a Character object for the given ID
"""
raise NotImplementedError()
def get_itemtype(self, type_id):
"""
:return: an ItemType object for the given ID
"""
raise NotImplemented()
@python_2_unicode_compatible
class EveSwaggerProvider(EveProvider):
@@ -135,13 +234,13 @@ class EveSwaggerProvider(EveProvider):
def __str__(self):
return 'esi'
def get_alliance(self, id):
def get_alliance(self, alliance_id):
try:
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=id).result()
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=id).result()
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result()
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result()
model = Alliance(
self.adapter,
id,
alliance_id,
data['alliance_name'],
data['ticker'],
corps,
@@ -149,14 +248,14 @@ class EveSwaggerProvider(EveProvider):
)
return model
except HTTPNotFound:
raise ObjectNotFound(id, 'alliance')
raise ObjectNotFound(alliance_id, 'alliance')
def get_corp(self, id):
def get_corp(self, corp_id):
try:
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=id).result()
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result()
model = Corporation(
self.adapter,
id,
corp_id,
data['corporation_name'],
data['ticker'],
data['ceo_id'],
@@ -167,20 +266,27 @@ class EveSwaggerProvider(EveProvider):
except HTTPNotFound:
raise ObjectNotFound(id, 'corporation')
def get_character(self, id):
def get_character(self, character_id):
try:
data = self.client.Character.get_characters_character_id(character_id=id).result()
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
alliance_id = self.adapter.get_corp(data['corporation_id']).alliance_id
model = Character(
self.adapter,
id,
character_id,
data['name'],
data['corporation_id'],
alliance_id,
)
return model
except (HTTPNotFound, HTTPUnprocessableEntity):
raise ObjectNotFound(id, 'character')
raise ObjectNotFound(character_id, 'character')
def get_itemtype(self, type_id):
try:
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()
return ItemType(self.adapter, type_id, data['name'])
except (HTTPNotFound, HTTPUnprocessableEntity):
raise ObjectNotFound(type_id, 'type')
@python_2_unicode_compatible
@@ -250,34 +356,112 @@ class EveXmlProvider(EveProvider):
raise e
return self._build_character(charinfo)
def get_itemtype(self, type_id):
api = evelink.eve.EVE(api=self.api)
try:
type_name = api.type_name_from_id(type_id).result
assert type_name != 'Unknown Type'
return ItemType(self.adapter, type_id, type_name)
except AssertionError:
raise ObjectNotFound(type_id, 'itemtype')
class EveAdapter(EveProvider):
"""
Redirects queries to appropriate data source.
"""
def __init__(self, char_provider, corp_provider, alliance_provider):
def __init__(self, char_provider, corp_provider, alliance_provider, itemtype_provider):
self.char_provider = char_provider
self.corp_provider = corp_provider
self.alliance_provider = alliance_provider
self.itemtype_provider = itemtype_provider
self.char_provider.adapter = self
self.corp_provider.adapter = self
self.alliance_provider.adapter = self
self.itemtype_provider.adapter = self
def __repr__(self):
return "<{} (char:{}, corp:{}, alliance:{})>".format(self.__class__.__name__, str(self.char_provider), str(self.corp_provider), str(self.alliance_provider))
return "<{} (character:{} corp:{} alliance:{} itemtype:{})>".format(self.__class__.__name__,
str(self.char_provider),
str(self.corp_provider),
str(self.alliance_provider),
str(self.itemtype_provider))
@staticmethod
def _get_from_cache(obj_class, id):
data = cache.get('%s__%s' % (obj_class.__name__.lower(), id))
if data:
obj = obj_class.from_dict(json.loads(data))
logger.debug('Got from cache: %s' % obj.__repr__())
return obj
else:
return None
@staticmethod
def _cache(obj):
logger.debug('Caching: %s ' % obj.__repr__())
cache.set('%s__%s' % (obj.__class__.__name__.lower(), obj.id), json.dumps(obj.serialize()),
int(OBJ_CACHE_DURATION))
def get_character(self, id):
return self.char_provider.get_character(id)
obj = self._get_from_cache(Character, id)
if obj:
obj.provider = self
else:
obj = self._get_character(id)
self._cache(obj)
return obj
def get_corp(self, id):
return self.corp_provider.get_corp(id)
obj = self._get_from_cache(Corporation, id)
if obj:
obj.provider = self
else:
obj = self._get_corp(id)
self._cache(obj)
return obj
def get_alliance(self, id):
obj = self._get_from_cache(Alliance, id)
if obj:
obj.provider = self
else:
obj = self._get_alliance(id)
self._cache(obj)
return obj
def get_itemtype(self, type_id):
obj = self._get_from_cache(ItemType, type_id)
if obj:
obj.provider = self
else:
obj = self._get_itemtype(type_id)
self._cache(obj)
return obj
def _get_character(self, id):
return self.char_provider.get_character(id)
def _get_corp(self, id):
return self.corp_provider.get_corp(id)
def _get_alliance(self, id):
return self.alliance_provider.get_alliance(id)
def _get_itemtype(self, type_id):
return self.itemtype_provider.get_itemtype(type_id)
def eve_adapter_factory(character_source=settings.EVEONLINE_CHARACTER_PROVIDER, corp_source=settings.EVEONLINE_CORP_PROVIDER, alliance_source=settings.EVEONLINE_ALLIANCE_PROVIDER, api_key=None, token=None):
sources = [character_source, corp_source, alliance_source]
CHARACTER_PROVIDER = getattr(settings, 'EVEONLINE_CHARACTER_PROVIDER', '') or 'esi'
CORP_PROVIDER = getattr(settings, 'EVEONLINE_CORP_PROVIDER', '') or 'esi'
ALLIANCE_PROVIDER = getattr(settings, 'EVEONLINE_ALLIANCE_PROVIDER', '') or 'esi'
ITEMTYPE_PROVIDER = getattr(settings, 'EVEONLINE_ITEMTYPE_PROVIDER', '') or 'esi'
def eve_adapter_factory(character_source=CHARACTER_PROVIDER, corp_source=CORP_PROVIDER,
alliance_source=ALLIANCE_PROVIDER, itemtype_source=ITEMTYPE_PROVIDER, api_key=None, token=None):
sources = [character_source, corp_source, alliance_source, itemtype_source]
providers = []
if 'xml' in sources:
@@ -292,4 +476,4 @@ def eve_adapter_factory(character_source=settings.EVEONLINE_CHARACTER_PROVIDER,
providers.append(esi)
else:
raise ValueError('Unrecognized data source "%s"' % source)
return EveAdapter(providers[0], providers[1], providers[2])
return EveAdapter(providers[0], providers[1], providers[2], providers[3])

View File

@@ -12,7 +12,6 @@ from services.managers.eve_api_manager import EveApiManager
from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo
from eveonline.models import EveAllianceInfo
from eveonline.providers import eve_adapter_factory
from authentication.tasks import set_state
import logging
import evelink

View File

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from eveonline.forms import UpdateKeyForm
from eveonline.managers import EveManager
from authentication.managers import AuthServicesInfoManager
@@ -11,7 +10,6 @@ from eveonline.models import EveApiKeyPair, EveCharacter
from authentication.models import AuthServicesInfo
from authentication.tasks import set_state
from eveonline.tasks import refresh_api
from esi.decorators import token_required
from django.conf import settings
import logging
@@ -30,7 +28,7 @@ def add_api_key(request):
api_key=form.cleaned_data['api_key']).exists():
# 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'])
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()
@@ -47,7 +45,8 @@ def add_api_key(request):
owner = request.user
# Grab characters associated with the key pair
characters = EveManager.get_characters_from_api(api_key)
[EveManager.create_character_obj(c, owner, api_key.api_id) for c in characters if not EveCharacter.objects.filter(character_id=c.id).exists()]
[EveManager.create_character_obj(c, owner, api_key.api_id) for c in characters if
not EveCharacter.objects.filter(character_id=c.id).exists()]
logger.info("Successfully processed api add form for user %s" % request.user)
if not settings.API_SSO_VALIDATION:
messages.success(request, 'Added API key %s to your account.' % form.cleaned_data['api_id'])
@@ -57,7 +56,7 @@ def add_api_key(request):
return redirect("auth_dashboard")
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})
return render(request, 'registered/apisso.html', context={'api': api_key})
else:
logger.debug("Form invalid: returning to form.")
else:
@@ -93,8 +92,9 @@ def api_sso_validate(request, token, api_id):
return redirect('auth_characters')
return redirect('auth_dashboard')
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})
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
@@ -110,7 +110,7 @@ def dashboard_view(request):
api_chars.append({
'id': api.api_id,
'sso_verified': api.sso_verified if sso_validation else True,
'characters': EveManager.get_characters_by_api_id(api.api_id),
'characters': EveCharacter.objects.filter(api_id=api.api_id),
})
context = {
@@ -139,7 +139,7 @@ def api_key_removal(request, api_id):
@login_required
def characters_view(request):
logger.debug("characters_view called by user %s" % request.user)
render_items = {'characters': EveManager.get_characters_by_owner_id(request.user.id),
render_items = {'characters': EveCharacter.objects.filter(user=request.user),
'authinfo': AuthServicesInfo.objects.get(user=request.user)}
return render(request, 'registered/characters.html', context=render_items)
@@ -147,7 +147,8 @@ def characters_view(request):
@login_required
def main_character_change(request, 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 EveCharacter.objects.filter(character_id=char_id).exists() and EveCharacter.objects.get(
character_id=char_id).user == request.user:
AuthServicesInfoManager.update_main_char_id(char_id, request.user)
messages.success(request, 'Changed main character ID to %s' % char_id)
set_state(request.user)

View File

@@ -28,14 +28,15 @@ logger = logging.getLogger(__name__)
FATS_PER_PAGE = int(getattr(settings, 'FATS_PER_PAGE', 20))
def get_page(model_list, page_num):
p = Paginator(model_list, FATS_PER_PAGE)
try:
fats = p.page(page_num)
except PageNotAnInteger:
fatss = p.page(1)
fats = p.page(1)
except EmptyPage:
fatss = p.page(p.num_pages)
fats = p.page(p.num_pages)
return fats
@@ -45,7 +46,8 @@ class CorpStat(object):
self.corp = corp
else:
self.corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
self.n_fats = Fat.objects.filter(character__corporation_id=self.corp.corporation_id).filter(fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
self.n_fats = Fat.objects.filter(character__corporation_id=self.corp.corporation_id).filter(
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
self.blue = self.corp.is_blue
def avg_fat(self):
@@ -95,7 +97,6 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
start_of_previous_month = first_day_of_previous_month(year, month)
fat_stats = {}
# get FAT stats for member corps
for corp_id in settings.STR_CORP_IDS:
@@ -110,8 +111,9 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
fatlink__fatdatetime__lt=start_of_next_month).exclude(character__corporation_id__in=fat_stats)
for fat in fats_in_span:
if not fat.character.corporation_id in fat_stats:
fat_stats[fat.character.corporation_id] = CorpStat(fat.character.corporation_id, start_of_month, start_of_next_month)
if fat.character.corporation_id not in fat_stats:
fat_stats[fat.character.corporation_id] = CorpStat(fat.character.corporation_id, start_of_month,
start_of_next_month)
# collect and sort stats
stat_list = [fat_stats[x] for x in fat_stats]
@@ -129,7 +131,7 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
@login_required
def fatlink_personal_statistics_view(request, year=datetime.date.today().year, main_name=None):
def fatlink_personal_statistics_view(request, year=datetime.date.today().year):
year = int(year)
logger.debug("Personal statistics view for year %i called by %s" % (year, request.user))
@@ -191,7 +193,8 @@ def fatlink_monthly_personal_statistics_view(request, year, month, char_id=None)
@login_required
@token_required(scopes=['esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-universe.read_structures.v1'])
@token_required(
scopes=['esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-universe.read_structures.v1'])
def click_fatlink_view(request, token, hash, fatname):
try:
fatlink = Fatlink.objects.filter(hash=hash)[0]
@@ -205,14 +208,20 @@ def click_fatlink_view(request, token, hash, fatname):
c = token.get_esi_client()
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
location['solar_system_name'] = c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()['solar_system_name']
location['solar_system_name'] = \
c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()[
'solar_system_name']
if location['structure_id']:
location['station_name'] = c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()['name']
location['station_name'] = \
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
'name']
elif location['station_id']:
location['station_name'] = c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['station_name']
location['station_name'] = \
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()[
'station_name']
else:
location['station_name'] = "No Station"
ship['ship_type_name'] = c.Universe.get_universe_types_type_id(type_id=ship['ship_type_id']).result()['type_name']
ship['ship_type_name'] = EveManager.get_itemtype(ship['ship_type_id']).name
fat = Fat()
fat.system = location['solar_system_name']

View File

@@ -16,12 +16,15 @@ django>=1.10,<2.0
django-bootstrap-form
django-navhelper
django-bootstrap-pagination
django-redis>=4.4
# awating release for fix to celery/django-celery#447
# django-celery
git+https://github.com/celery/django-celery
git+git://github.com/nikdoof/python-ts3.git
git+https://github.com/pyghassen/openfire-restapi
# awating pyghassen/openfire-restapi #1 to fix installation issues
git+https://github.com/adarnof/openfire-restapi
git+https://github.com/adarnof/adarnauth-esi

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-01-18 20:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('srp', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='srpuserrequest',
name='srp_status',
field=models.CharField(choices=[('Pending', 'Pending'), ('Approved', 'Approved'), ('Rejected', 'Rejected')], default='Pending', max_length=8),
),
]

View File

@@ -16,15 +16,29 @@ class SrpFleetMain(models.Model):
fleet_srp_aar_link = models.CharField(max_length=254, default="")
def __str__(self):
return self.fleet_name + " - SrpFleetMain"
return self.fleet_name
@property
def total_cost(self):
return sum([int(r.srp_total_amount) for r in self.srpuserrequest_set.all()])
@property
def pending_requests(self):
return self.srpuserrequest_set.filter(srp_status='Pending').count()
@python_2_unicode_compatible
class SrpUserRequest(models.Model):
SRP_STATUS_CHOICES = (
('Pending', 'Pending'),
('Approved', 'Approved'),
('Rejected', 'Rejected'),
)
killboard_link = models.CharField(max_length=254, default="")
after_action_report_link = models.CharField(max_length=254, default="")
additional_info = models.CharField(max_length=254, default="")
srp_status = models.CharField(max_length=254, default="")
srp_status = models.CharField(max_length=8, default="Pending", choices=SRP_STATUS_CHOICES)
srp_total_amount = models.BigIntegerField(default=0)
character = models.ForeignKey(EveCharacter)
srp_fleet_main = models.ForeignKey(SrpFleetMain)
@@ -33,4 +47,4 @@ class SrpUserRequest(models.Model):
post_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.character.character_name + " - SrpUserRequest"
return self.character.character_name + ' SRP request for ' + self.srp_ship_name

View File

@@ -15,7 +15,6 @@ from services.managers.srp_manager import srpManager
from notifications import notify
from django.utils import timezone
from authentication.decorators import members_and_blues
from esi.clients import esi_client_factory
import uuid
import logging
@@ -35,20 +34,9 @@ def random_string(string_length=10):
@members_and_blues()
def srp_management(request):
logger.debug("srp_management called by user %s" % request.user)
totalcost = 0
runningcost = 0
price_pair = {}
for fleet_main in SrpFleetMain.objects.filter(fleet_srp_status="").iterator():
for fleet_data in SrpUserRequest.objects.filter(srp_fleet_main=fleet_main).iterator():
totalcost = totalcost + fleet_data.srp_total_amount
runningcost = runningcost + fleet_data.srp_total_amount
price_pair[fleet_main.id] = runningcost
logger.debug("Determined SRP fleet %s costs %s" % (fleet_main.id, runningcost))
runningcost = 0
logger.debug("Determined total outstanding SRP cost %s" % totalcost)
context = {"srpfleets": SrpFleetMain.objects.filter(fleet_srp_status=""), "totalcost": totalcost,
"price_pair": price_pair}
fleets = SrpFleetMain.objects.filter(fleet_srp_status="")
totalcost = sum([int(fleet.total_cost) for fleet in fleets])
context = {"srpfleets": fleets, "totalcost": totalcost}
return render(request, 'registered/srpmanagement.html', context=context)
@@ -56,19 +44,9 @@ def srp_management(request):
@members_and_blues()
def srp_management_all(request):
logger.debug("srp_management_all called by user %s" % request.user)
totalcost = 0
runningcost = 0
price_pair = {}
for fleet_main in SrpFleetMain.objects.all().iterator():
for fleet_data in SrpUserRequest.objects.filter(srp_fleet_main=fleet_main).iterator():
totalcost = totalcost + fleet_data.srp_total_amount
runningcost = runningcost + fleet_data.srp_total_amount
price_pair[fleet_main.id] = runningcost
logger.debug("Determined SRP fleet %s costs %s" % (fleet_main.id, runningcost))
runningcost = 0
logger.debug("Determined all-time total SRP cost %s" % totalcost)
context = {"srpfleets": SrpFleetMain.objects.all(), "totalcost": totalcost, "price_pair": price_pair}
fleets = SrpFleetMain.objects.all()
totalcost = sum([int(fleet.total_cost) for fleet in fleets])
context = {"srpfleets": SrpFleetMain.objects.all(), "totalcost": totalcost}
return render(request, 'registered/srpmanagement.html', context=context)
@@ -78,14 +56,9 @@ def srp_fleet_view(request, fleet_id):
logger.debug("srp_fleet_view called by user %s for fleet id %s" % (request.user, fleet_id))
if SrpFleetMain.objects.filter(id=fleet_id).exists():
fleet_main = SrpFleetMain.objects.get(id=fleet_id)
totalcost = 0
for fleet_data in SrpUserRequest.objects.filter(srp_fleet_main=fleet_main).filter(srp_status="Approved"):
totalcost = totalcost + fleet_data.srp_total_amount
logger.debug("Determiend fleet id %s total cost %s" % (fleet_id, totalcost))
context = {"fleet_id": fleet_id, "fleet_status": fleet_main.fleet_srp_status,
"srpfleetrequests": SrpUserRequest.objects.filter(srp_fleet_main=fleet_main),
"totalcost": totalcost}
"srpfleetrequests": fleet_main.srpuserrequest_set.all(),
"totalcost": fleet_main.total_cost}
return render(request, 'registered/srpfleetdata.html', context=context)
else:
@@ -236,18 +209,16 @@ def srp_request_view(request, fleet_srp):
character = EveManager.get_character_by_id(authinfo.main_char_id)
srp_fleet_main = SrpFleetMain.objects.get(fleet_srp_code=fleet_srp)
post_time = timezone.now()
srp_status = "Pending"
srp_request = SrpUserRequest()
srp_request.killboard_link = form.cleaned_data['killboard_link']
srp_request.additional_info = form.cleaned_data['additional_info']
srp_request.character = character
srp_request.srp_fleet_main = srp_fleet_main
srp_request.srp_status = srp_status
try:
srp_kill_link = srpManager.get_kill_id(srp_request.killboard_link)
(srp_kill_data, ship_value) = srpManager.get_kill_data(srp_kill_link)
(ship_type_id, ship_value) = srpManager.get_kill_data(srp_kill_link)
except ValueError:
logger.debug("User %s Submitted Invalid Killmail Link %s or server could not be reached" % (
request.user, srp_request.killboard_link))
@@ -255,8 +226,7 @@ def srp_request_view(request, fleet_srp):
messages.error(request,
"Your SRP request Killmail link is invalid. Please make sure you are using zKillboard.")
return redirect("auth_srp_management_view")
c = esi_client_factory()
srp_ship_name = c.Universe.get_universe_types_type_id(type_id=srp_kill_data).result()['type_name']
srp_ship_name = EveManager.get_itemtype(ship_type_id).name
srp_request.srp_ship_name = srp_ship_name
kb_total_loss = ship_value
srp_request.kb_total_loss = kb_total_loss

View File

@@ -32,13 +32,7 @@
<html>
<head lang="en">
<meta charset="UTF-8">
<title>
{% if IS_CORP %}
{{ CORP_NAME }}
{% else %}
{{ ALLIANCE_NAME }}
{% endif %}
</title>
<title>{{ SITE_NAME }}</title>
</head>
<body>
<div id="logo">

View File

@@ -11,12 +11,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
{% if IS_CORP %}
<title>{{ CORP_NAME }} - Login</title>
{% else %}
<title>{{ ALLIANCE_NAME }} - Login</title>
{% endif %}
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom Fonts -->

View File

@@ -10,12 +10,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
{% if IS_CORP %}
<title>{{ CORP_NAME }} - Login</title>
{% else %}
<title>{{ ALLIANCE_NAME }} - Login</title>
{% endif %}
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">

View File

@@ -22,7 +22,7 @@
</div>
<div class="col-lg-4 col-sm-2">
<table class="table">
<tr><td class="text-center"><img class="ra-avatar" src="http://image.eveonline.com/Corporation/{{ main.corporation_id }}_128.png"></td></tr>
<tr><td class="text-center"><img class="ra-avatar" src="https://image.eveonline.com/Corporation/{{ main.corporation_id }}_128.png"></td></tr>
<tr><td class="text-center">{{ main.corporation_name }}</td></tr>
</table>
</div>

View File

@@ -51,11 +51,7 @@
<div class="alert alert-warning text-center">No groups available.</div>
{% endif %}
{% else %}
{% if IS_CORP %}
<div class="alert alert-danger" role="alert">{% trans "You are not in the corporation." %}</div>
{% else %}
<div class="alert alert-danger" role="alert">{% trans "You are not in the alliance." %}</div>
{% endif %}
<div class="alert alert-danger" role="alert">{% trans "You are not a member." %}</div>
{% endif %}
</div>

View File

@@ -51,14 +51,10 @@
</tr>
{% for srpfleetrequest in srpfleetrequests %}
<tr>
<td class="text-center">
<div class="label label-info">
{{ srpfleetrequest.character.character_name }}
</div>
</td>
<td class="text-center">{{ srpfleetrequest.character.character_name }}</td>
<td class="text-center">
<a href="{{ srpfleetrequest.killboard_link }}"
target="_blank">{{ srpfleetrequest.killboard_link }}</a>
target="_blank" class="label label-warning">Link</a>
</td>
<td class="text-center">{{ srpfleetrequest.additional_info }}</td>
<td class="text-center">{{ srpfleetrequest.srp_ship_name }}</td>
@@ -83,36 +79,28 @@
{% if perms.auth.srp_management %}
<td class="text-center">
<a href="{% url 'auth_srp_request_update_amount_view' srpfleetrequest.id %}" class="btn btn-info">
<span class="glyphicon glyphicon-pencil"></span>
<a href="{% url 'auth_srp_request_update_amount_view' srpfleetrequest.id %}" class="btn btn-info" title="Update Value">
<span class="glyphicon glyphicon-usd"></span>
</a>
{% if srpfleetrequest.srp_status == "Rejected" %}
<a href="{% url 'auth_srp_request_approve' srpfleetrequest.id %}" class="btn btn-success">
<span class="glyphicon glyphicon-ok"></span>
</a>
{% elif srpfleetrequest.srp_status == "Pending" %}
<a href="{% url 'auth_srp_request_approve' srpfleetrequest.id %}" class="btn btn-success">
{% if srpfleetrequest.srp_status in "RejectedPending" %}
<a href="{% url 'auth_srp_request_approve' srpfleetrequest.id %}" class="btn btn-success" title="Approve">
<span class="glyphicon glyphicon-ok"></span>
</a>
{% elif srpfleetrequest.srp_status == "" %}
<a href="{% url 'auth_srp_request_approve' srpfleetrequest.id %}" class="btn btn-success">
<a href="{% url 'auth_srp_request_approve' srpfleetrequest.id %}" class="btn btn-success" title="Approve">
<span class="glyphicon glyphicon-ok"></span>
</a>
{% endif %}
{% if srpfleetrequest.srp_status == "Approved" %}
<a href="{% url 'auth_srp_request_reject' srpfleetrequest.id %}" class="btn btn-warning">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% elif srpfleetrequest.srp_status == "Pending" %}
<a href="{% url 'auth_srp_request_reject' srpfleetrequest.id %}" class="btn btn-warning">
{% if srpfleetrequest.srp_status in "ApprovedPending" %}
<a href="{% url 'auth_srp_request_reject' srpfleetrequest.id %}" class="btn btn-warning" title="Reject">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% elif srpfleetrequest.srp_status == "" %}
<a href="{% url 'auth_srp_request_reject' srpfleetrequest.id %}" class="btn btn-warning">
<a href="{% url 'auth_srp_request_reject' srpfleetrequest.id %}" class="btn btn-warning" title="Reject">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
<a href="{% url 'auth_srp_request_remove' srpfleetrequest.id %}" class="btn btn-danger">
<a href="{% url 'auth_srp_request_remove' srpfleetrequest.id %}" class="btn btn-danger" title="Remove">
<span class="glyphicon glyphicon-trash"></span>
</a>

View File

@@ -40,6 +40,7 @@
<th class="text-center">{% trans "Fleet SRP Code" %}</th>
<th class="text-center">{% trans "Fleet ISK Cost" %}</th>
<th class="text-center">{% trans "SRP Status" %}</th>
<th class="text-center">{% trans "Pending Requests" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
{% for srpfleet in srpfleets %}
@@ -56,27 +57,21 @@
{{ srpfleet.fleet_commander.character_name }}
</div>
</td>
<th class="text-center"><a href="{{ srpfleet.fleet_srp_aar_link }}"
target="_blank">{{ srpfleet.fleet_srp_aar_link }}</a></th>
<th class="text-center">
<td class="text-center">
{% if srpfleet.fleet_srp_aar_link %}
<a href="{{ srpfleet.fleet_srp_aar_link }}" target="_blank" class="label label-primary">{% trans 'Link' %}</a>
{% endif %}
</td>
<td class="text-center">
{% if srpfleet.fleet_srp_code %}
<div class="label label-warning">
<a href="{% url 'auth_srp_request_view' srpfleet.fleet_srp_code %}"
>{{ srpfleet.fleet_srp_code }}</a>
</div>
<a class="label label-warning" href="{% url 'auth_srp_request_view' srpfleet.fleet_srp_code %}">{{ srpfleet.fleet_srp_code }}</a>
{% else %}
<div class="label label-danger">
{% trans "Disabled" %}
</div>
{% endif %}
</th>
<td class="text-center">
{% for key,value in price_pair.items %}
{% if key == srpfleet.id %}
ISK: {{ value | intcomma }}
{% endif %}
{% endfor %}
</td>
<td class="text-center">ISK: {{ srpfleet.total_cost | intcomma }}</td>
<td class="text-center">
{% if srpfleet.fleet_srp_status == "" %}
@@ -89,26 +84,29 @@
</div>
{% endif %}
</td>
<td class="text-center">
<div class="label label-warning">{{ srpfleet.pending_requests }}</div>
</td>
<td class="text-center">
<a href="{% url 'auth_srp_fleet_view' srpfleet.id %}" class="btn btn-primary">
<a href="{% url 'auth_srp_fleet_view' srpfleet.id %}" class="btn btn-primary" title="View">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
{% if perms.auth.srp_management %}
<a href="{% url 'auth_srp_fleet_edit_view' srpfleet.id %}" class="btn btn-info">
<a href="{% url 'auth_srp_fleet_edit_view' srpfleet.id %}" class="btn btn-info" title="Edit">
<span class="glyphicon glyphicon-pencil"></span>
</a>
<a href="{% url 'auth_srp_fleet_remove' srpfleet.id %}" class="btn btn-danger">
<a href="{% url 'auth_srp_fleet_remove' srpfleet.id %}" class="btn btn-danger" title="Remove">
<span class="glyphicon glyphicon-trash"></span>
</a>
{% if srpfleet.fleet_srp_code %}
<a href="{% url 'auth_srp_fleet_disable' srpfleet.id %}" class="btn btn-warning">
<a href="{% url 'auth_srp_fleet_disable' srpfleet.id %}" class="btn btn-warning" title="Disable">
<span class="glyphicon glyphicon-remove-sign"></span>
</a>
{% else %}
<a href="{% url 'auth_srp_fleet_enable' srpfleet.id %}" class="btn btn-success">
<a href="{% url 'auth_srp_fleet_enable' srpfleet.id %}" class="btn btn-success" title="Enable">
<span class="glyphicon glyphicon-ok-sign"></span>
</a>
{% endif %}

View File

@@ -12,11 +12,7 @@
<meta name="description" content="">
<meta name="author" content="">
{% if IS_CORP %}
<title>{{ CORP_NAME }} - Login</title>
{% else %}
<title>{{ ALLIANCE_NAME }} - Login</title>
{% endif %}
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">

View File

@@ -12,11 +12,7 @@
<meta name="description" content="">
<meta name="author" content="">
{% if IS_CORP %}
<title>{{ CORP_NAME }} - Login</title>
{% else %}
<title>{{ ALLIANCE_NAME }} - Login</title>
{% endif %}
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom Fonts -->

View File

@@ -12,7 +12,7 @@
<meta name="description" content="">
<meta name="author" content="">
<title>{{ CORP_NAME }} - Login</title>
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">

View File

@@ -11,12 +11,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
{% if IS_CORP %}
<title>{{ CORP_NAME }} - Login</title>
{% else %}
<title>{{ ALLIANCE_NAME }} - Login</title>
{% endif %}
<title>{{ SITE_NAME }} - Login</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">