diff --git a/dashboard/settings.py b/dashboard/settings.py
index de34d7a..229089b 100644
--- a/dashboard/settings.py
+++ b/dashboard/settings.py
@@ -43,6 +43,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles",
"accounts",
"dash",
+ "support",
]
MIDDLEWARE = [
@@ -136,6 +137,11 @@ HOSTEA = {
"RESTRICT_NEW_INTEGRATION_INSTALLATION": True,
"INSTANCE_MAINTAINER_CONTACT": "contact@hostea.example.org",
"ACCOUNTS": {"MAX_VERIFICATION_TOLERANCE_PERIOD": 60 * 60 * 24}, # in seconds
+ "META": {
+ "GITEA_INSTANCE": "https://gitea.hostea.org",
+ "GITEA_ORG_NAME": "Hostea",
+ "SUPPORT_REPOSITORY": "support",
+ },
}
EMAIL_CONFIG = env.email("EMAIL_URL", default="smtp://admin:password@localhost:10025")
diff --git a/dashboard/urls.py b/dashboard/urls.py
index 2394c0a..dc9d8d1 100644
--- a/dashboard/urls.py
+++ b/dashboard/urls.py
@@ -19,5 +19,6 @@ from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("dash/", include("dash.urls")),
+ path("support/", include("support.urls")),
path("", include("accounts.urls")),
]
diff --git a/support/__init__.py b/support/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/support/admin.py b/support/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/support/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/support/apps.py b/support/apps.py
new file mode 100644
index 0000000..dd1d778
--- /dev/null
+++ b/support/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class SupportConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "support"
diff --git a/support/migrations/__init__.py b/support/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/support/models.py b/support/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/support/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/support/templates/support/index.html b/support/templates/support/index.html
new file mode 100644
index 0000000..ce6446f
--- /dev/null
+++ b/support/templates/support/index.html
@@ -0,0 +1,6 @@
+{% extends 'dash/common/base.html' %}
+{% block dash %}
+{% include "common/components/error.html" %}
+
{{ title }}
+ {% include "common/components/error.html" %}
+{% endblock %}
diff --git a/support/templates/support/list.html b/support/templates/support/list.html
new file mode 100644
index 0000000..430fcca
--- /dev/null
+++ b/support/templates/support/list.html
@@ -0,0 +1,9 @@
+{% extends 'dash/common/base.html' %} {% block dash %}
+{{ title }}
+
+
+ You will be redirected to Hostea's issue tracker
+ momentarily. If not, please click
+ here.
+
+{% include "support/redirect.html" %} {% endblock %}
diff --git a/support/templates/support/new.html b/support/templates/support/new.html
new file mode 100644
index 0000000..2521236
--- /dev/null
+++ b/support/templates/support/new.html
@@ -0,0 +1,9 @@
+{% extends 'dash/common/base.html' %} {% block dash %}
+{{ title }}
+
+
+ You will be redirected to Hostea's issue tracker
+ momentarily. If not, please click
+ here.
+
+{% include "support/redirect.html" %} {% endblock %}
diff --git a/support/templates/support/redirect.html b/support/templates/support/redirect.html
new file mode 100644
index 0000000..10c265a
--- /dev/null
+++ b/support/templates/support/redirect.html
@@ -0,0 +1,38 @@
+
diff --git a/support/tests.py b/support/tests.py
new file mode 100644
index 0000000..f04e8cf
--- /dev/null
+++ b/support/tests.py
@@ -0,0 +1,138 @@
+# Copyright © 2022 Aravinth Manivannan
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from django.contrib.auth import get_user_model
+from django.utils.http import urlencode
+from django.urls import reverse
+from django.test import TestCase, Client, override_settings
+from django.contrib.auth import authenticate
+from django.conf import settings
+
+from .utils import IssueTracker
+
+
+hostea_issue_tracker_settings = settings.HOSTEA
+hostea_issue_tracker_settings["META"] = {
+ "GITEA_INSTANCE": "https://gitea.hostea.org",
+ "GITEA_ORG_NAME": "Hostea",
+ "SUPPORT_REPOSITORY": "support",
+}
+
+
+@override_settings(HOSTEA=hostea_issue_tracker_settings)
+class IssueTrackerTests(TestCase):
+ """
+ Test IssueTracker utility
+ """
+
+ def test_defaults(self):
+ """
+ Verify default credentials; all further tests are based on defaults set
+ """
+ it = IssueTracker()
+ self.assertEqual(it.config["GITEA_INSTANCE"], "https://gitea.hostea.org")
+ self.assertEqual(it.config["GITEA_ORG_NAME"], "Hostea")
+ self.assertEqual(it.config["SUPPORT_REPOSITORY"], "support")
+
+ def test_uri_builders(self):
+ """
+ Verify default credentials; all further tests are based on defaults set
+ """
+ it = IssueTracker()
+ self.assertEqual(
+ it.get_issue_tracker(), "https://gitea.hostea.org/Hostea/support/issues"
+ )
+ self.assertEqual(
+ it.open_issue(), "https://gitea.hostea.org/Hostea/support/issues/new"
+ )
+
+
+class SupportWorks(TestCase):
+ """
+ Tests create new app view
+ """
+
+ def setUp(self):
+ self.password = "password121231"
+ self.username = "suport_user"
+ self.email = f"{self.username}@example.org"
+ self.user = get_user_model().objects.create(
+ username=self.username,
+ email=self.email,
+ )
+ self.user.set_password(self.password)
+ self.user.save()
+
+ def test_dash_is_protected(self):
+ """
+ Tests if support templates render
+ """
+ # default LOGIN redirect URI that is used by @login_required decorator is
+ # /accounts/login. There's a redirection endpoint at /accounts/login/ that
+ # will redirect user to /login. Hence the /accounts prefix
+ def redirect_login_uri(uri: str) -> str:
+ return f"/accounts{reverse('accounts.login')}?next={uri}"
+
+ urls = [
+ reverse("support.home"),
+ reverse("support.new"),
+ reverse("support.view"),
+ ]
+ for i in urls:
+ print(f"[*] Testing URI: {i}")
+ resp = self.client.get(i)
+ self.assertEqual(resp.status_code, 302)
+ expected = redirect_login_uri(i)
+ self.assertEqual(resp.headers["location"], expected)
+
+ def test_dash_home_renders(self):
+ """
+ Tests if login template renders
+ """
+ c = Client()
+
+ # username login works
+ payload = {
+ "login": self.username,
+ "password": self.password,
+ }
+ resp = c.post(reverse("accounts.login"), payload)
+ self.assertEqual(resp.status_code, 302)
+ self.assertEqual(resp.headers["location"], reverse("accounts.home"))
+
+ urls = [
+ reverse("support.home"),
+ reverse("support.new"),
+ reverse("support.view"),
+ ]
+ for i in urls:
+ print(f"[*] Testing URI: {i}")
+ resp = c.get(i)
+ self.assertEqual(resp.status_code, 200)
+ self.assertEqual(b"Billing" in resp.content, True)
+ self.assertEqual(b"Support" in resp.content, True)
+ self.assertEqual(b"Logout" in resp.content, True)
+
+ # new issue view
+ resp = c.get(reverse("support.new"))
+ self.assertEqual(resp.status_code, 200)
+ it = IssueTracker()
+ new_issue = str.encode(it.open_issue())
+ self.assertEqual(new_issue in resp.content, True)
+
+ # list issues view
+ resp = c.get(reverse("support.new"))
+ self.assertEqual(resp.status_code, 200)
+ issue_tracker = str.encode(it.get_issue_tracker())
+ self.assertEqual(issue_tracker in resp.content, True)
diff --git a/support/urls.py b/support/urls.py
new file mode 100644
index 0000000..3892e0c
--- /dev/null
+++ b/support/urls.py
@@ -0,0 +1,24 @@
+# Copyright © 2022 Aravinth Manivannan
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from django.contrib import admin
+from django.urls import path, include
+
+from .views import home, new_ticket, view_tickets
+
+urlpatterns = [
+ path("new/", new_ticket, name="support.new"),
+ path("view/", view_tickets, name="support.view"),
+ path("", home, name="support.home"),
+]
diff --git a/support/utils.py b/support/utils.py
new file mode 100644
index 0000000..ada7525
--- /dev/null
+++ b/support/utils.py
@@ -0,0 +1,48 @@
+# Copyright © 2022 Aravinth Manivannan
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from urllib.parse import urlparse, urlunparse
+
+from django.conf import settings
+
+
+class IssueTracker:
+ """
+ Hostea support repository Issue tracker URL generation stuff
+ """
+
+ def __init__(self):
+ self.config = settings.HOSTEA["META"]
+ self.instance = urlparse(self.config["GITEA_INSTANCE"])
+ self.repo = (
+ f"{self.config['GITEA_ORG_NAME']}/{self.config['SUPPORT_REPOSITORY']}"
+ )
+ self.issues = f"{self.repo}/issues"
+
+ def __path(self, path=str):
+ i = self.instance
+ return urlunparse((i.scheme, i.netloc, path, "", "", ""))
+
+ def get_issue_tracker(self):
+ """
+ Get issue tracker URL
+ """
+ return self.__path(path=self.issues)
+
+ def open_issue(self):
+ """
+ Get open new issue URL
+ """
+ path = f"{self.issues}/new"
+ return self.__path(path=path)
diff --git a/support/views.py b/support/views.py
new file mode 100644
index 0000000..0011745
--- /dev/null
+++ b/support/views.py
@@ -0,0 +1,67 @@
+# Copyright © 2022 Aravinth Manivannan
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from django.shortcuts import render, redirect
+from django.contrib.auth.decorators import login_required
+
+from .utils import IssueTracker
+
+
+def default_ctx(title: str, username: str):
+ """
+ Default context for all dashboard pages
+ """
+ it = IssueTracker()
+ return {
+ "title": title,
+ "username": username,
+ "open_support": "open",
+ "support": {"list": it.get_issue_tracker(), "new": it.open_issue()},
+ }
+
+
+@login_required
+def home(request):
+ """
+ Support page view
+ """
+ PAGE_TITLE = "Support"
+ username = request.user
+ ctx = default_ctx(title=PAGE_TITLE, username=username.username)
+ return render(request, "support/index.html", context=ctx)
+
+
+@login_required
+def new_ticket(request):
+ """
+ Support page view
+ """
+ PAGE_TITLE = "New Ticket"
+ username = request.user
+ it = IssueTracker()
+ ctx = default_ctx(title=PAGE_TITLE, username=username.username)
+ return render(request, "support/new.html", context=ctx)
+
+
+@login_required
+def view_tickets(request):
+ """
+ Support page view
+ """
+ PAGE_TITLE = "Opened Tickets"
+ username = request.user
+ it = IssueTracker()
+ ctx = default_ctx(title=PAGE_TITLE, username=username.username)
+ ctx["support"] = {"list": it.get_issue_tracker(), "new": it.open_issue()}
+ return render(request, "support/list.html", context=ctx)