From e29c1d3295b44650a3661c85e90c5b05aded7684 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sun, 17 Apr 2016 15:17:32 -0400 Subject: [PATCH] Discourse (#377) * Initial work on Discourse integration * Views for discourse * Discourse group updates Correct password display * Removed password functions Changed delete to suspend user forever Added unsuspend check to add_user --- alliance_auth/settings.py.example | 16 ++ alliance_auth/urls.py | 4 + authentication/managers.py | 12 + authentication/models.py | 2 + celerytask/signals.py | 4 + celerytask/tasks.py | 21 ++ services/managers/discourse_manager.py | 338 +++++++++++++++++++++++ services/models.py | 9 + services/views.py | 36 ++- stock/templates/registered/services.html | 38 +++ util/context_processors.py | 3 + 11 files changed, 481 insertions(+), 2 deletions(-) create mode 100644 services/managers/discourse_manager.py diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index e01d1176..bfa42806 100755 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -249,6 +249,7 @@ BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'Fals # ENABLE_AUTH_MUMBLE - Enable mumble support in the auth for auth'd members # ENABLE_AUTH_IPBOARD - Enable IPBoard forum support in the auth for auth'd members # ENABLE_AUTH_DISCORD - Enable Discord support in the auth for auth'd members +# ENABLE_AUTH_DISCOURSE - Enable Discourse support in the auth for auth'd members # 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 @@ -259,10 +260,12 @@ ENABLE_AUTH_MUMBLE = 'True' == os.environ.get('AA_ENABLE_AUTH_MUMBLE', 'False') ENABLE_AUTH_IPBOARD = 'True' == os.environ.get('AA_ENABLE_AUTH_IPBOARD', 'False') ENABLE_AUTH_TEAMSPEAK3 = 'True' == os.environ.get('AA_ENABLE_AUTH_TEAMSPEAK3', 'False') ENABLE_AUTH_DISCORD = 'True' == os.environ.get('AA_ENABLE_AUTH_DISCORD', 'False') +ENABLE_AUTH_DISCOURSE = 'True' == os.environ.get('AA_ENABLE_AUTH_DISCOURSE', 'False') ENABLE_AUTH_IPS4 = 'True' == os.environ.get('AA_ENABLE_AUTH_IPS4', 'False') ENABLE_AUTH_SMF = 'True' == os.environ.get('AA_ENABLE_AUTH_SMF', 'False') ENABLE_AUTH_MARKET = 'True' == os.environ.get('AA_ENABLE_AUTH_MARKET', 'False') + ##################### # Blue service Setup ##################### @@ -272,6 +275,7 @@ ENABLE_AUTH_MARKET = 'True' == os.environ.get('AA_ENABLE_AUTH_MARKET', 'False') # ENABLE_BLUE_MUMBLE - Enable mumble support in the auth for blues # ENABLE_BLUE_IPBOARD - Enable IPBoard forum support in the auth for blues # ENABLE_BLUE_DISCORD - Enable Discord support in the auth for blues +# ENABLE_BLUE_DISCOURSE - Enable Discord support in the auth for blues # 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 @@ -283,6 +287,7 @@ ENABLE_BLUE_MUMBLE = 'True' == os.environ.get('AA_ENABLE_BLUE_MUMBLE', 'False') ENABLE_BLUE_IPBOARD = 'True' == os.environ.get('AA_ENABLE_BLUE_IPBOARD', 'False') ENABLE_BLUE_TEAMSPEAK3 = 'True' == os.environ.get('AA_ENABLE_BLUE_TEAMSPEAK3', 'False') ENABLE_BLUE_DISCORD = 'True' == os.environ.get('AA_ENABLE_BLUE_DISCORD', 'False') +ENABLE_BLUE_DISCOURSE = 'True' == os.environ.get('AA_ENABLE_BLUE_DISCOURSE', 'False') ENABLE_BLUE_IPS4 = 'True' == os.environ.get('AA_ENABLE_BLUE_IPS4', 'False') ENABLE_BLUE_SMF = 'True' == os.environ.get('AA_ENABLE_BLUE_SMF', 'False') ENABLE_BLUE_MARKET = 'True' == os.environ.get('AA_ENABLE_BLUE_MARKET', 'False') @@ -406,6 +411,17 @@ DISCORD_SERVER_ID = os.environ.get('AA_DISCORD_SERVER_ID', '') DISCORD_USER_EMAIL = os.environ.get('AA_DISCORD_USER_EMAIL', '') DISCORD_USER_PASSWORD = os.environ.get('AA_DISCORD_USER_PASSWORD', '') +###################################### +# Discourse Configuration +###################################### +# DISCOURSE_URL - Web address of the forums (no trailing slash) +# DISCOURSE_API_USERNAME - API account username +# DISCOURSE_API_KEY - API Key +###################################### +DISCOURSE_URL = os.environ.get('AA_DISCOURSE_URL', '') +DISCOURSE_API_USERNAME = os.environ.get('AA_DISCOURSE_API_USERNAME', '') +DISCOURSE_API_KEY = os.environ.get('AA_DISCOURSE_API_KEY', '') + ##################################### # IPS4 Configuration ##################################### diff --git a/alliance_auth/urls.py b/alliance_auth/urls.py index 6016057c..ca15a232 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -137,6 +137,10 @@ urlpatterns = patterns('', url(r'^deactivate_discord/$', 'services.views.deactivate_discord', name='auth_deactivate_discord'), url(r'^reset_discord/$', 'services.views.reset_discord', name='auth_reset_discord'), + # Discourse Service Control + url(r'^activate_discourse/$', 'services.views.activate_discourse', name='auth_activate_discourse'), + url(r'^deactivate_discourse/$', 'services.views.deactivate_discourse', name='auth_deactivate_discourse'), + # IPS4 Service Control url(r'^activate_ips4/$', 'services.views.activate_ips4', name='auth_activate_ips4'), diff --git a/authentication/managers.py b/authentication/managers.py index 5ce01acf..926b6ca1 100755 --- a/authentication/managers.py +++ b/authentication/managers.py @@ -122,6 +122,18 @@ class AuthServicesInfoManager: else: logger.error("Failed to update user %s discord info: user does not exist." % user) + @staticmethod + def update_user_discourse_info(username, password, user): + if User.objects.filter(username=user.username).exists(): + logger.debug("Updating user %s discourse info: username %s" % (user, username)) + authserviceinfo = AuthServicesInfoManager.__get_or_create(user) + authserviceinfo.discourse_username = username + authserviceinfo.discourse_password = password + authserviceinfo.save(update_fields=['discourse_username', 'discourse_password']) + logger.info("Updated user %s discourse info in authservicesinfo model." % user) + else: + logger.error("Failed to update user %s discourse info: user does not exist." % user) + @staticmethod def update_user_ips4_info(username, password, id, user): if User.objects.filter(username=user.username).exists(): diff --git a/authentication/models.py b/authentication/models.py index 5a7df68b..713f658f 100755 --- a/authentication/models.py +++ b/authentication/models.py @@ -14,6 +14,8 @@ class AuthServicesInfo(models.Model): teamspeak3_uid = models.CharField(max_length=254, blank=True, default="") teamspeak3_perm_key = models.CharField(max_length=254, blank=True, default="") discord_uid = models.CharField(max_length=254, blank=True, default="") + discourse_username = models.CharField(max_length=254, blank=True, default="") + discourse_password = models.CharField(max_length=254, blank=True, default="") ips4_username = models.CharField(max_length=254, blank=True, default="") ips4_password = models.CharField(max_length=254, blank=True, default="") ips4_id = models.CharField(max_length=254, blank=True, default="") diff --git a/celerytask/signals.py b/celerytask/signals.py index 48830303..f2fcd60e 100644 --- a/celerytask/signals.py +++ b/celerytask/signals.py @@ -10,6 +10,7 @@ from .tasks import update_forum_groups from .tasks import update_ipboard_groups from .tasks import update_discord_groups from .tasks import update_teamspeak3_groups +from .tasks import update_discourse_groups from .tasks import update_smf_groups from authentication.models import AuthServicesInfo from services.models import AuthTS @@ -36,6 +37,8 @@ def m2m_changed_user_groups(sender, instance, action, *args, **kwargs): update_discord_groups.delay(instance.pk) if auth.mumble_username: update_mumble_groups.delay(instance.pk) + if auth.discourse_username: + update_discourse_groups.delay(instance.pk) def trigger_all_ts_update(): for auth in AuthServicesInfo.objects.filter(teamspeak3_uid__isnull=False): @@ -56,3 +59,4 @@ def post_save_authts(sender, instance, *args, **kwargs): def post_delete_authts(sender, instance, *args, **kwargs): logger.debug("Received post_delete signal from %s" % instance) trigger_all_ts_update() + diff --git a/celerytask/tasks.py b/celerytask/tasks.py index 8bff12b0..866bd8cf 100755 --- a/celerytask/tasks.py +++ b/celerytask/tasks.py @@ -11,6 +11,7 @@ from services.managers.phpbb3_manager import Phpbb3Manager from services.managers.ipboard_manager import IPBoardManager from services.managers.teamspeak3_manager import Teamspeak3Manager from services.managers.discord_manager import DiscordManager, DiscordAPIManager +from services.managers.discourse_manager import DiscourseManager from services.managers.smf_manager import smfManager from services.models import AuthTS from services.models import TSgroup @@ -181,6 +182,26 @@ def update_discord_groups(pk): raise self.retry(countdown = 60 * 10) logger.debug("Updated user %s discord groups." % user) +@task +def update_discourse_groups(pk): + user = User.objects.get(pk=pk) + logger.debug("Updating discourse groups for user %s" % user) + authserviceinfo = AuthServicesInfo.objects.get(user=user) + groups = [] + for group in user.groups.all(): + groups.append(str(group.name)) + if len(groups) == 0: + logger.debug("No syncgroups found for user. Adding empty group.") + groups.append('empty') + logger.debug("Updating user %s discord groups to %s" % (user, groups)) + try: + DiscourseManager.update_groups(authserviceinfo.discourse_username, groups) + except: + logger.warn("Discourse group sync failed for %s, retrying in 10 mins" % user, exc_info=True) + raise self.retry(countdown = 60 * 10) + logger.debug("Updated user %s discord groups." % user) + + def assign_corp_group(auth): corp_group = None if auth.main_char_id: diff --git a/services/managers/discourse_manager.py b/services/managers/discourse_manager.py new file mode 100644 index 00000000..047963fa --- /dev/null +++ b/services/managers/discourse_manager.py @@ -0,0 +1,338 @@ +import logging +import requests +import os +import datetime +import json +from django.conf import settings +from django.utils import timezone +from services.models import GroupCache + +logger = logging.getLogger(__name__) + +# not exhaustive, only the ones we need +ENDPOINTS = { + 'groups': { + 'list': { + 'path': "/admin/groups.json", + 'method': requests.get, + 'args': { + 'required': [], + 'optional': [], + }, + }, + 'create': { + 'path': "/admin/groups", + 'method': requests.post, + 'args': { + 'required': ['name'], + 'optional': ['visible'], + } + }, + 'add_user': { + 'path': "/admin/groups/%s/members.json", + 'method': requests.put, + 'args': { + 'required': ['usernames'], + 'optional': [], + }, + }, + 'remove_user': { + 'path': "/admin/groups/%s/members.json", + 'method': requests.delete, + 'args': { + 'required': ['username'], + 'optional': [], + }, + }, + 'delete': { + 'path': "/admin/groups/%s.json", + 'method': requests.delete, + 'args': { + 'required': [], + 'optional': [], + }, + }, + }, + 'users': { + 'create': { + 'path': "/users", + 'method': requests.post, + 'args': { + 'required': ['name', 'email', 'password', 'username'], + 'optional': ['active'], + }, + }, + 'update': { + 'path': "/users/%s.json", + 'method': requests.put, + 'args': { + 'required': ['params'], + 'optional': [], + } + }, + 'get': { + 'path': "/users/%s.json", + 'method': requests.get, + 'args': { + 'required': [], + 'optional': [], + }, + }, + 'activate': { + 'path': "/admin/users/%s/activate", + 'method': requests.put, + 'args': { + 'required': [], + 'optional': [], + }, + }, + 'set_email': { + 'path': "/users/%s/preferences/email", + 'method': requests.put, + 'args': { + 'required': ['email'], + 'optional': [], + }, + }, + 'suspend': { + 'path': "/admin/users/%s/suspend", + 'method': requests.put, + 'args': { + 'required': ['duration', 'reason'], + 'optional': [], + }, + }, + 'unsuspend': { + 'path': "/admin/users/%s/unsuspend", + 'method': requests.put, + 'args': { + 'required': [], + 'optional': [], + }, + }, + }, +} + +class DiscourseManager: + GROUP_CACHE_MAX_AGE = datetime.timedelta(minutes=30) + REVOKED_EMAIL = 'revoked@' + settings.DOMAIN + SUSPEND_DAYS = 99999 + SUSPEND_REASON = "Disabled by auth." + + @staticmethod + def __exc(endpoint, *args, **kwargs): + params = { + 'api_key': settings.DISCOURSE_API_KEY, + 'api_username': settings.DISCOURSE_API_USERNAME, + } + if args: + path = endpoint['path'] % args + else: + path = endpoint['path'] + data = {} + for arg in endpoint['args']['required']: + data[arg] = kwargs[arg] + for arg in endpoint['args']['optional']: + if arg in kwargs: + data[arg] = kwargs[arg] + for arg in kwargs: + if not arg in endpoint['args']['required'] and not arg in endpoint['args']['optional']: + logger.warn("Received unrecognized kwarg %s for endpoint %s" % (arg, endpoint)) + r = endpoint['method'](settings.DISCOURSE_URL + path, params=params, json=data) + out = r.text + try: + if 'errors' in r.json(): + logger.error("Discourse execution failed.\nEndpoint: %s\nErrors: %s" % (endpoint, r.json()['errors'])) + r.raise_for_status() + if 'success' in r.json(): + if not r.json()['success']: + raise Exception("Execution failed") + out = r.json() + except ValueError: + logger.warn("No json data received for endpoint %s" % endpoint) + r.raise_for_status() + return out + + @staticmethod + def __generate_random_pass(): + return os.urandom(8).encode('hex') + + @staticmethod + def __get_groups(): + endpoint = ENDPOINTS['groups']['list'] + data = DiscourseManager.__exc(endpoint) + return [g for g in data if not g['automatic']] + + @staticmethod + def __update_group_cache(): + GroupCache.objects.filter(service="discourse").delete() + cache = GroupCache.objects.create(service="discourse") + cache.groups = json.dumps(DiscourseManager.__get_groups()) + cache.save() + return cache + + @staticmethod + def __get_group_cache(): + if not GroupCache.objects.filter(service="discourse").exists(): + DiscourseManager.__update_group_cache() + cache = GroupCache.objects.get(service="discourse") + age = timezone.now() - cache.created + if age > DiscourseManager.GROUP_CACHE_MAX_AGE: + logger.debug("Group cache has expired. Triggering update.") + cache = DiscourseManager.__update_group_cache() + return json.loads(cache.groups) + + @staticmethod + def __create_group(name): + endpoint = ENDPOINTS['groups']['create'] + DiscourseManager.__exc(endpoint, name=name[:20], visible=True) + DiscourseManager.__update_group_cache() + + @staticmethod + def __group_name_to_id(name): + cache = DiscourseManager.__get_group_cache() + for g in cache: + if g['name'] == name[0:20]: + return g['id'] + logger.debug("Group %s not found on Discourse. Creating" % name) + DiscourseManager.__create_group(name) + return DiscourseManager.__group_name_to_id(name) + + @staticmethod + def __group_id_to_name(id): + cache = DiscourseManager.__get_group_cache() + for g in cache: + if g['id'] == id: + return g['name'] + raise KeyError("Group ID %s not found on Discourse" % id) + + @staticmethod + def __add_user_to_group(id, username): + endpoint = ENDPOINTS['groups']['add_user'] + DiscourseManager.__exc(endpoint, id, usernames=[username]) + + @staticmethod + def __remove_user_from_group(id, username): + endpoint = ENDPOINTS['groups']['remove_user'] + DiscourseManager.__exc(endpoint, id, username=username) + + @staticmethod + def __generate_group_dict(names): + dict = {} + for name in names: + dict[name] = DiscourseManager.__group_name_to_id(name) + return dict + + @staticmethod + def __get_user_groups(username): + data = DiscourseManager.__get_user(username) + return [g['id'] for g in data['user']['groups'] if not g['automatic']] + + @staticmethod + def __user_name_to_id(name): + data = DiscourseManager.__get_user(name) + return data['user']['id'] + + @staticmethod + def __user_id_to_name(id): + raise NotImplementedError + + @staticmethod + def __get_user(username): + endpoint = ENDPOINTS['users']['get'] + return DiscourseManager.__exc(endpoint, username) + + @staticmethod + def __activate_user(username): + endpoint = ENDPOINTS['users']['activate'] + id = DiscourseManager.__user_name_to_id(username) + DiscourseManager.__exc(endpoint, id) + + @staticmethod + def __update_user(username, **kwargs): + endpoint = ENDPOINTS['users']['update'] + id = DiscourseManager.__user_name_to_id(username) + DiscourseManager.__exc(endpoint, id, params=kwargs) + + @staticmethod + def __create_user(username, email, password): + endpoint = ENDPOINTS['users']['create'] + DiscourseManager.__exc(endpoint, name=username, username=username, email=email, password=password, active=True) + + @staticmethod + def __check_if_user_exists(username): + try: + DiscourseManager.__user_name_to_id(username) + return True + except: + return False + + @staticmethod + def __suspend_user(username): + id = DiscourseManager.__user_name_to_id(username) + endpoint = ENDPOINTS['users']['suspend'] + return DiscourseManager.__exc(endpoint, id, duration=DiscourseManager.SUSPEND_DAYS, reason=DiscourseManager.SUSPEND_REASON) + + @staticmethod + def __unsuspend(username): + id = DiscourseManager.__user_name_to_id(username) + endpoint = ENDPOINTS['users']['unsuspend'] + return DiscourseManager.__exc(endpoint, id) + + @staticmethod + def __set_email(username, email): + endpoint = ENDPOINTS['users']['set_email'] + return DiscourseManager.__exc(endpoint, username, email=email) + + @staticmethod + def _sanatize_username(username): + sanatized = username.replace(" ", "_") + sanatized = sanatized.replace("'", "") + return sanatized + + @staticmethod + def add_user(username, email): + logger.debug("Adding new discourse user %s" % username) + password = DiscourseManager.__generate_random_pass() + safe_username = DiscourseManager._sanatize_username(username) + try: + if DiscourseManager.__check_if_user_exists(safe_username): + logger.debug("Discourse user %s already exists. Reactivating" % safe_username) + DiscourseManager.__unsuspend(safe_username) + else: + logger.debug("Creating new user account for %s" % username) + DiscourseManager.__create_user(safe_username, email, password) + logger.info("Added new discourse user %s" % username) + return safe_username, password + except: + logger.exception("Failed to add new discourse user %s" % username) + return "","" + + @staticmethod + def delete_user(username): + logger.debug("Deleting discourse user %s" % username) + try: + DiscourseManager.__suspend_user(username) + logger.info("Deleted discourse user %s" % username) + return True + except: + logger.exception("Failed to delete discourse user %s" % username) + return False + + @staticmethod + def update_groups(username, raw_groups): + groups = [] + for g in raw_groups: + groups.append(g[:20]) + logger.debug("Updating discourse user %s groups to %s" % (username, groups)) + group_dict = DiscourseManager.__generate_group_dict(groups) + inv_group_dict = {v:k for k,v in group_dict.items()} + user_groups = DiscourseManager.__get_user_groups(username) + add_groups = [group_dict[x] for x in group_dict if not group_dict[x] in user_groups] + rem_groups = [x for x in user_groups if not inv_group_dict[x] in groups] + if add_groups or rem_groups: + logger.info("Updating discourse user %s groups: adding %s, removing %s" % (username, add_groups, rem_groups)) + for g in add_groups: + DiscourseManager.__add_user_to_group(g, username) + for g in rem_groups: + DiscourseManager.__remove_user_from_group(g, username) diff --git a/services/models.py b/services/models.py index 7a69db70..47b3cdd8 100644 --- a/services/models.py +++ b/services/models.py @@ -46,3 +46,12 @@ class MumbleUser(models.Model): def __str__(self): return self.username + +class GroupCache(models.Model): + SERVICE_CHOICES = ( + ("discourse", "discourse"), + ) + + created = models.DateTimeField(auto_now_add=True) + groups = models.TextField(blank=True, null=True) + service = models.CharField(max_length=254, choices=SERVICE_CHOICES, unique=True) diff --git a/services/views.py b/services/views.py index 15f3697a..fd8534a6 100755 --- a/services/views.py +++ b/services/views.py @@ -14,6 +14,7 @@ from managers.mumble_manager import MumbleManager from managers.ipboard_manager import IPBoardManager from managers.teamspeak3_manager import Teamspeak3Manager from managers.discord_manager import DiscordManager +from managers.discourse_manager import DiscourseManager from managers.ips4_manager import Ips4Manager from managers.smf_manager import smfManager from managers.market_manager import marketManager @@ -26,6 +27,7 @@ from celerytask.tasks import update_ipboard_groups from celerytask.tasks import update_smf_groups from celerytask.tasks import update_teamspeak3_groups from celerytask.tasks import update_discord_groups +from celerytask.tasks import update_discourse_groups from forms import JabberBroadcastForm from forms import FleetFormatterForm from forms import DiscordForm @@ -608,6 +610,37 @@ def set_ipboard_password(request): context = {'form': form, 'service': 'IPBoard', 'error': error} return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request)) +@login_required +@user_passes_test(service_blue_alliance_test) +def activate_discourse(request): + logger.debug("activate_discourse called by user %s" % request.user) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + character = EveManager.get_character_by_id(authinfo.main_char_id) + logger.debug("Adding discourse user for user %s with main character %s" % (request.user, character)) + result = DiscourseManager.add_user(character.character_name, request.user.email) + if result[0] != "": + AuthServicesInfoManager.update_user_discourse_info(result[0], result[1], request.user) + logger.debug("Updated authserviceinfo for user %s with discourse credentials. Updating groups." % request.user) + update_discourse_groups.delay(request.user.pk) + logger.info("Successfully activated discourse for user %s" % request.user) + return HttpResponseRedirect("/services/") + logger.error("Unsuccessful attempt to activate forum for user %s" % request.user) + return HttpResponseRedirect("/dashboard") + + +@login_required +@user_passes_test(service_blue_alliance_test) +def deactivate_discourse(request): + logger.debug("deactivate_discourse called by user %s" % request.user) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + result = DiscourseManager.delete_user(authinfo.discourse_username) + if result: + AuthServicesInfoManager.update_user_discourse_info("", "", request.user) + logger.info("Successfully deactivated discourse for user %s" % request.user) + return HttpResponseRedirect("/services/") + logger.error("Unsuccessful attempt to activate discourse for user %s" % request.user) + return HttpResponseRedirect("/dashboard") + @login_required @user_passes_test(service_blue_alliance_test) def activate_ips4(request): @@ -721,7 +754,6 @@ def deactivate_smf(request): logger.error("Unsuccesful attempt to activate smf for user %s" % request.user) return HttpResponseRedirect("/dashboard") - @login_required @user_passes_test(service_blue_alliance_test) def reset_smf_password(request): @@ -844,4 +876,4 @@ def set_market_password(request): logger.debug("Rendering form for user %s" % request.user) context = {'form': form, 'service': 'Market'} - return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request)) \ No newline at end of file + return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request)) diff --git a/stock/templates/registered/services.html b/stock/templates/registered/services.html index 1ff6b002..1e7cac52 100755 --- a/stock/templates/registered/services.html +++ b/stock/templates/registered/services.html @@ -236,6 +236,25 @@ {% endif %} + {% if ENABLE_BLUE_DISCOURSE %} + Discourse + {{ authinfo.discourse_username }} + {{ authinfo.discourse_password }} + {{ DISCOURSE_URL }} + + {% ifequal authinfo.discourse_username "" %} + + + + {% else %} + + + + {% endifequal %} + + {% endif %} {% if ENABLE_BLUE_TEAMSPEAK3 %} Service @@ -507,6 +526,25 @@ {% endif %} + {% if ENABLE_AUTH_DISCOURSE %} + Discourse + {{ authinfo.discourse_username }} + {{ authinfo.discourse_password }} + {{ DISCOURSE_URL }} + + {% ifequal authinfo.discourse_username "" %} + + + + {% else %} + + + + {% endifequal %} + + {% endif %} {% if ENABLE_AUTH_TEAMSPEAK3 %} Service diff --git a/util/context_processors.py b/util/context_processors.py index c9a74e19..524217ad 100755 --- a/util/context_processors.py +++ b/util/context_processors.py @@ -37,6 +37,7 @@ def domain_url(request): 'ENABLE_AUTH_IPBOARD': settings.ENABLE_AUTH_IPBOARD, 'ENABLE_AUTH_TEAMSPEAK3': settings.ENABLE_AUTH_TEAMSPEAK3, 'ENABLE_AUTH_DISCORD': settings.ENABLE_AUTH_DISCORD, + 'ENABLE_AUTH_DISCOURSE': settings.ENABLE_AUTH_DISCOURSE, 'ENABLE_AUTH_IPS4': settings.ENABLE_AUTH_IPS4, 'ENABLE_AUTH_SMF': settings.ENABLE_AUTH_SMF, 'ENABLE_AUTH_MARKET': settings.ENABLE_AUTH_MARKET, @@ -46,6 +47,7 @@ def domain_url(request): 'ENABLE_BLUE_IPBOARD': settings.ENABLE_BLUE_IPBOARD, 'ENABLE_BLUE_TEAMSPEAK3': settings.ENABLE_BLUE_TEAMSPEAK3, 'ENABLE_BLUE_DISCORD': settings.ENABLE_BLUE_DISCORD, + 'ENABLE_BLUE_DISCOURSE': settings.ENABLE_BLUE_DISCOURSE, 'ENABLE_BLUE_IPS4': settings.ENABLE_BLUE_IPS4, 'ENABLE_BLUE_SMF': settings.ENABLE_BLUE_SMF, 'ENABLE_BLUE_MARKET': settings.ENABLE_BLUE_MARKET, @@ -53,6 +55,7 @@ def domain_url(request): 'JACK_KNIFE_URL': settings.JACK_KNIFE_URL, 'DISCORD_SERVER_ID': settings.DISCORD_SERVER_ID, 'KILLBOARD_URL': settings.KILLBOARD_URL, + 'DISCOURSE_URL': settings.DISCOURSE_URL, 'IPS4_URL': settings.IPS4_URL, 'SMF_URL': settings.SMF_URL, 'MARKET_URL': settings.MARKET_URL,