diff --git a/memberportal/api_general/views.py b/memberportal/api_general/views.py index e004f813..a14ff517 100644 --- a/memberportal/api_general/views.py +++ b/memberportal/api_general/views.py @@ -37,7 +37,6 @@ def get(self, request): "memberbucks_topup_options": json.loads( config.STRIPE_MEMBERBUCKS_TOPUP_OPTIONS ), - "trelloIntegration": config.ENABLE_TRELLO_INTEGRATION, "enableProxyVoting": config.ENABLE_PROXY_VOTING, "enableStripe": config.ENABLE_STRIPE and len(config.STRIPE_PUBLISHABLE_KEY) > 0 diff --git a/memberportal/api_member_tools/views.py b/memberportal/api_member_tools/views.py index 4dfb962a..ef0cb15e 100644 --- a/memberportal/api_member_tools/views.py +++ b/memberportal/api_member_tools/views.py @@ -100,7 +100,7 @@ def get(self, request): class IssueDetail(APIView): """ - post: Creates a new issue by creating a trello card or emailing the management committee + post: Creates a new issue by creating a task card or emailing the management committee """ permission_classes = (permissions.IsAuthenticated,) @@ -113,60 +113,172 @@ def post(self, request): if not (title and description): return Response(status=status.HTTP_400_BAD_REQUEST) - use_trello = config.ENABLE_TRELLO_INTEGRATION - trello_key = config.TRELLO_API_KEY - trello_token = config.TRELLO_API_TOKEN - trello_id_list = config.TRELLO_ID_LIST - - if use_trello: - url = "https://api.trello.com/1/cards" - - querystring = { - "name": title, - "desc": description, - "pos": "top", - "idList": trello_id_list, - "keepFromSource": "all", - "key": trello_key, - "token": trello_token, - } + failed = False - response = requests.request("POST", url, params=querystring) + request.user.log_event( + "Submitted issue: " + title + " Content: " + description, + "generic", + ) - if response.status_code == 200: - request.user.log_event( - "Submitted issue: " + title + " Content: " + description, - "generic", - ) + if config.REPORT_ISSUE_ENABLE_VIKUNJA and bool( + config.VIKUNJA_DEFAULT_PROJECT_ID + ): + vikunja_project_id = config.VIKUNJA_DEFAULT_PROJECT_ID + vikunja_label_id = config.VIKUNJA_DEFAULT_LABEL_ID + + try: + task_body = { + "max_right": None, + "id": 0, + "title": title, + "description": description, + "done": False, + "done_at": None, + "priority": 0, + "labels": [], + "assignees": [], + "due_date": None, + "start_date": None, + "end_date": None, + "repeat_after": 0, + "repeat_from_current_date": False, + "repeat_mode": 0, + "reminders": [], + "parent_task_id": 0, + "hex_color": "", + "percent_done": 0, + "related_tasks": {}, + "attachments": [], + "cover_image_attachment_id": None, + "identifier": "", + "index": 0, + "is_favorite": False, + "subscription": None, + "position": 64, + "reactions": {}, + "created_by": { + "max_right": None, + "id": 0, + "email": "", + "username": "", + "name": "", + "exp": 0, + "type": 0, + "created": None, + "updated": None, + "settings": { + "max_right": None, + "name": "", + "email_reminders_enabled": False, + "discoverable_by_name": False, + "discoverable_by_email": True, + "overdue_tasks_reminders_enabled": False, + "week_start": 0, + "timezone": "", + "language": "en", + "frontend_settings": { + "play_sound_when_done": False, + "quick_add_magic_mode": "vikunja", + "color_schema": "auto", + "default_view": "first", + }, + }, + }, + "created": "1970-01-01T00:00:00.000Z", + "updated": "1970-01-01T00:00:00.000Z", + "project_id": vikunja_project_id, + "bucket_id": 0, + "reminder_dates": None, + } - return Response( - {"success": True, "url": response.json()["shortUrl"]}, - status=status.HTTP_201_CREATED, + task_response = requests.request( + "PUT", + f"{config.VIKUNJA_URL}/api/v1/projects/{vikunja_project_id}/tasks", + json=task_body, + headers={"Authorization": "Bearer " + config.VIKUNJA_API_TOKEN}, ) - else: - return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) + if (vikunja_label_id is not None) and ( + task_response.status_code == 200 + ): + try: + task_id = task_response.json["id"] + label_body = { + "label_id": vikunja_label_id, + "created": "1970-01-01T00:00:00.000Z", + } + + label_response = requests.request( + "PUT", + f"{config.VIKUNJA_URL}/api/v1/tasks/{task_id}/labels", + json=label_body, + headers={ + "Authorization": "Bearer " + config.VIKUNJA_API_TOKEN + }, + ) + except Exception: + pass + + if task_response.status_code != 200: + failed = True + + except Exception: + # uh oh, but don't stop processing other ones + failed = True + + if config.REPORT_ISSUE_ENABLE_TRELLO: + try: + trello_key = config.TRELLO_API_KEY + trello_token = config.TRELLO_API_TOKEN + trello_id_list = config.TRELLO_ID_LIST + trello_url = "https://api.trello.com/1/cards" + + querystring = { + "name": title, + "desc": description, + "pos": "top", + "idList": trello_id_list, + "keepFromSource": "all", + "key": trello_key, + "token": trello_token, + } - # if Trello isn't configured, use email instead + response = requests.request("POST", trello_url, params=querystring) + + if response.status_code != 200: + failed = True + + except Exception: + # uh oh, but don't stop processing other ones + failed = True + + # email report + if config.REPORT_ISSUE_ENABLE_EMAIL: + try: + subject = f"{request.user.profile.get_full_name()}: {title}" + + if not send_email_to_admin( + subject=subject, + template_vars={ + "title": subject, + "message": description, + }, + user=request.user, + reply_to=request.user.email, + ): + failed = True + + except Exception: + # uh oh, but don't stop processing other ones + failed = True + + if failed: + return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: - subject = f"{request.user.profile.get_full_name()} submitted an issue about {title}" - - if send_email_to_admin( - subject=subject, - template_vars={ - "title": subject, - "message": description, - }, - user=request.user, - reply_to=request.user.email, - ): - return Response( - {"success": True}, - status=status.HTTP_201_CREATED, - ) - - else: - return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response( + {"success": True}, + status=status.HTTP_201_CREATED, + ) class MeetingList(APIView): diff --git a/memberportal/membermatters/constance_config.py b/memberportal/membermatters/constance_config.py index eb32922c..8be57585 100644 --- a/memberportal/membermatters/constance_config.py +++ b/memberportal/membermatters/constance_config.py @@ -120,15 +120,33 @@ False, "Enable integration with stripe for membership payments.", ), + # ==== Report Issue Services ==== + # Email config + "REPORT_ISSUE_ENABLE_EMAIL": ( + True, + "Enable the submit issue to email integration.", + ), # Vikunja config + "REPORT_ISSUE_ENABLE_VIKUNJA": ( + False, + "Enable the submit issue to Vikunja integration.", + ), + "VIKUNJA_DEFAULT_PROJECT_ID": ( + "", + "Set this to the ID of your default project to create issues in.", + ), + "VIKUNJA_DEFAULT_LABEL_ID": ( + "", + "[optional] Set this to the ID of your default label if you want new issues to be tagged.", + ), "VIKUNJA_TEAMS": ( '[{"name": "Members", "oidcID": "members", "description": "The default team for all members.", "isPublic": false}]', "A JSON array of Vikunja teams to add users to when they login via SSO. Returned as an OIDC claim with the 'vikunja_teams' scope. Check Vikunja docs for syntax.", ), # Trello config - "ENABLE_TRELLO_INTEGRATION": ( + "REPORT_ISSUE_ENABLE_TRELLO": ( False, - "Enable the submit issue to trello integration. If disabled we'll send an email to EMAIL_ADMIN instead.", + "Enable the submit issue to trello integration.", ), "TRELLO_API_KEY": ("", "Set this to your Trello API key."), "TRELLO_API_TOKEN": ("", "Set this to your Trello API token."), @@ -432,11 +450,25 @@ "MEMBERBUCKS_CURRENCY", ), ), - ("Vikunja Integration", ("VIKUNJA_TEAMS",)), + ( + "Report Issue Services", + ( + "REPORT_ISSUE_ENABLE_EMAIL", + "REPORT_ISSUE_ENABLE_VIKUNJA", + "REPORT_ISSUE_ENABLE_TRELLO", + ), + ), + ( + "Vikunja Integration", + ( + "VIKUNJA_TEAMS", + "VIKUNJA_DEFAULT_PROJECT_ID", + "VIKUNJA_DEFAULT_LABEL_ID", + ), + ), ( "Trello Integration", ( - "ENABLE_TRELLO_INTEGRATION", "TRELLO_API_KEY", "TRELLO_API_TOKEN", "TRELLO_ID_LIST",