diff --git a/company/templates/company/add_domain.html b/company/templates/company/add_domain.html index 8a8e92c23..0041f2dfb 100644 --- a/company/templates/company/add_domain.html +++ b/company/templates/company/add_domain.html @@ -136,13 +136,18 @@

Domain Information Upload a webshot - +

or drag and drop

PNG, JPG, GIF up to 10MB

+
@@ -152,13 +157,13 @@

Domain Managers

Add Emails which will be allowed to start hunts and manage reported bugs. Email should be domain mail and should be registerd user on BLT.

- -
- +
+
@@ -199,9 +204,6 @@

Notifications

- -
@@ -69,14 +75,3 @@
All Domains
{% endblock body %} -{% block js %} - -{% endblock js %} diff --git a/company/templates/company/edit_domain.html b/company/templates/company/edit_domain.html new file mode 100644 index 000000000..ba9edfca4 --- /dev/null +++ b/company/templates/company/edit_domain.html @@ -0,0 +1,364 @@ +{% extends "company/company_dashboard_base.html" %} +{% block title %} + Edit Domain +{% endblock title %} +{% block body %} +
+
+

Edit Domain

+
+
+ {% csrf_token %} + + +
+

Domain Information

+

Use a permanent address where you can receive mail.

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+ + +
+
+ +
+
+ {% if domain.webshot %} + Webshot + {% else %} + + {% endif %} +
+ +

or drag and drop

+
+

PNG, JPG, GIF up to 10MB

+
+
+
+
+
+
+
+

Domain Managers

+

+ Add Emails which will be allowed to start hunts and manage reported bugs. + Email should be domain mail and should be registered user on BLT. +

+ +
+ + {% for manager in domain.managers.all %} +
+ + +
+ {% endfor %} +
+
+
+

Notifications

+

+ We'll always let you know about important changes, but you pick what + else you want to hear about. +

+
+
+
+
+
+ +
+
+ +

Get notified when someone reports a bug on this domain.

+
+
+
+
+ +
+
+ +

Get notified when a bug report is marked closed.

+
+
+
+
+
+
+
+ +
+
+
+{% endblock body %} +{% block js %} + +{% endblock js %} diff --git a/company/templates/company/view_domain.html b/company/templates/company/view_domain.html index d344dbcb6..1d22c1379 100644 --- a/company/templates/company/view_domain.html +++ b/company/templates/company/view_domain.html @@ -44,7 +44,7 @@ height="100%">
-
- + + + Manage Domain + @@ -89,12 +90,12 @@

Domain Info

  • Joined: - {{ created__day }}/{{ created__month }}/{{ created__year }} + {{ created|date:"d/m/Y" }}
  • Socials: - Domain Info - Domain Info - Domain Info

    Activity log

    - +
    -

    Profile informations changed.

    -

    3 min ago

    +

    Domain created at: {{ created|date:"d/m/Y" }}

    +

    {{ created|timesince }} ago

    - - +
    -

    - Connected with Colby Covington. -

    -

    15 min ago

    +

    Last modification in domain: {{ modified|date:"d/m/Y" }}

    +

    {{ modified|timesince }} ago

    - - -
    -
    -
    -
    -
    -

    - Invoice #4563 was created. -

    -

    57 min ago

    -
    -
    - - -
    -
    -
    -
    -
    -

    - Message received from Cecilia Hendric. -

    -

    1 hour ago

    -
    -
    - - -
    -
    -
    -
    -
    -

    - New order received #OR9653. -

    -

    2 hours ago

    -
    -
    - - -
    -
    -
    + {% if first_bug %} + +
    +
    +
    +
    +
    +

    First bug in this domain: {{ first_bug.created|date:"d/m/Y" }}

    +

    {{ first_bug.created|timesince }} ago

    +
    -
    -

    - Message received from Jane Stillman. -

    -

    2 hours ago

    + {% endif %} + {% if last_bug %} + +
    +
    +
    +
    +
    +

    Last bug created in this domain: {{ last_bug.created|date:"d/m/Y" }}

    +

    {{ last_bug.created|timesince }} ago

    +
    -
    - + {% endif %}
    @@ -312,48 +281,58 @@

    Statistics

    Ongoing Bughunts

    - View all + View all
    - {% for i in '123' %} -
    -
    - bughunt logo -
    -
    -

    Hunt 2.0

    -

    https://google.com

    -
    -

    ${{ hunt.prize }}

    - + {% if ongoing_bughunts %} + {% for hunt in ongoing_bughunts %} +
    +
    + bughunt logo +
    +
    +

    {{ hunt.name }}

    +

    {{ hunt.url }}

    +
    +

    + $ + {% if hunt.total_prize %} + {{ hunt.total_prize }} + {% else %} + 0 + {% endif %} +

    + More info +
    + {% endfor %} + {% else %} +
    +

    No ongoing bughunts available

    - {% endfor %} + {% endif %}
    -

    Latest Bugs Reported

    - View all +

    Latest Bugs Reported ({{ latest_issues|length }})

    + View all
    {% if latest_issues|length > 0 %} {% for issue in latest_issues %}
    - bug
    - Reported to {{ issue.domain__name }} on {{ issue.created__day }}/{{ issue.created__month }}/{{ issue.created__year }} + Reported to {{ issue.domain__name }} on {{ issue.created|date:"d/m/Y" }} Read more + href="{% url 'issue_view2' issue.id %}">Read more
    @@ -394,23 +373,13 @@

    {{ issue.user__username }}

    Top Testers ({{ top_testers | length }})

    - - - - - -
    {% for tester in top_testers %} - tester avatarTop Testers ({{ top_testers | length
    diff --git a/company/urls.py b/company/urls.py index 28af76ced..7a1c418dc 100644 --- a/company/urls.py +++ b/company/urls.py @@ -46,5 +46,10 @@ path("dashboard/end_bughunt/", EndBughuntView.as_view(), name="end_bughunt"), path("/dashboard/add_bughunt/", AddHuntView.as_view(), name="add_bughunt"), path("/dashboard/add_domain/", AddDomainView.as_view(), name="add_domain"), + path( + "/dashboard/edit_domain//", + AddDomainView.as_view(), + name="edit_domain", + ), path("domain//", login_required(DomainView.as_view()), name="view_domain"), ] diff --git a/company/views.py b/company/views.py index cef6c150f..558e87400 100644 --- a/company/views.py +++ b/company/views.py @@ -10,14 +10,14 @@ from django.core.files.storage import default_storage from django.core.validators import URLValidator from django.db import transaction -from django.db.models import Count, Q, Sum +from django.db.models import Count, OuterRef, Q, Subquery, Sum from django.db.models.functions import ExtractMonth from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from django.views.generic import View -from website.models import Company, Domain, Hunt, HuntPrize, Issue +from website.models import Company, Domain, Hunt, HuntPrize, Issue, IssueScreenshot restricted_domain = ["gmail.com", "hotmail.com", "outlook.com", "yahoo.com", "proton.com"] @@ -413,6 +413,8 @@ def dispatch(self, request, *args, **kwargs): if method == "delete": return self.delete(request, *args, **kwargs) + elif method == "put": + return self.put(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs) @@ -424,13 +426,21 @@ def get(self, request, id, *args, **kwargs): .distinct() ) + users = User.objects.filter(is_active=True) + domain_id = kwargs.get("domain_id") + domain = Domain.objects.filter(id=domain_id).first() if domain_id else None context = { "company": id, "company_obj": Company.objects.filter(id=id).first(), "companies": companies, + "users": users, + "domain": domain, # Pass the domain to the template if it exists } - return render(request, "company/add_domain.html", context=context) + if domain: + return render(request, "company/edit_domain.html", context=context) + else: + return render(request, "company/add_domain.html", context=context) @validate_company_user def post(self, request, id, *args, **kwargs): @@ -444,18 +454,18 @@ def post(self, request, id, *args, **kwargs): if domain_data["name"] is None: messages.error(request, "Enter domain name") - return redirect("add_domain", id) + return redirect("add_domain", id=id) if domain_data["url"] is None: messages.error(request, "Enter domain url") - return redirect("add_domain", id) + return redirect("add_domain", id=id) parsed_url = urlparse(domain_data["url"]) domain = (parsed_url.hostname).replace("www.", "") domain_data["name"] = domain_data["name"].lower() - managers_list = request.POST.getlist("email") + managers_list = request.POST.getlist("user") company_obj = Company.objects.get(id=id) domain_exist = Domain.objects.filter( @@ -464,7 +474,7 @@ def post(self, request, id, *args, **kwargs): if domain_exist: messages.error(request, "Domain name or url already exist.") - return redirect("add_domain", id) + return redirect("add_domain", id=id) # validate domain url try: @@ -474,62 +484,200 @@ def post(self, request, id, *args, **kwargs): response = requests.get(safe_url, timeout=5) if response.status_code != 200: raise Exception - except Exception: + except requests.exceptions.RequestException: messages.error(request, "Domain does not exist.") - return redirect("add_domain", id) - except Exception as e: - print(e) - messages.error(request, "Domain does not exist.") - return redirect("add_domain", id) + return redirect("add_domain", id=id) + except ValueError: + messages.error(request, "URL validation error.") + return redirect("add_domain", id=id) # validate domain email user_email_domain = request.user.email.split("@")[-1] - if domain != user_email_domain: - messages.error(request, "your email does not match domain email. Action Denied!") - return redirect("add_domain", id) + if not domain.endswith(f".{user_email_domain}") and domain != user_email_domain: + messages.error(request, "Your email does not match domain email. Action Denied!") + return redirect("add_domain", id=id) for domain_manager_email in managers_list: - user_email_domain = domain_manager_email.split("@")[-1] - if domain != user_email_domain: + manager_email_domain = domain_manager_email.split("@")[-1] + if not domain.endswith(f".{manager_email_domain}") and domain != manager_email_domain: messages.error( request, f"Manager: {domain_manager_email} does not match domain email." ) - return redirect("add_domain", id) - - domain_logo = request.FILES.get("logo") - domain_logo_file = domain_logo.name.split(".")[0] - extension = domain_logo.name.split(".")[-1] - domain_logo.name = domain_logo_file[:99] + str(uuid.uuid4()) + "." + extension - default_storage.save(f"logos/{domain_logo.name}", domain_logo) + return redirect("add_domain", id=id) + + if request.FILES.get("logo"): + domain_logo = request.FILES.get("logo") + domain_logo_file = domain_logo.name.split(".")[0] + extension = domain_logo.name.split(".")[-1] + domain_logo.name = domain_logo_file[:99] + str(uuid.uuid4()) + "." + extension + default_storage.save(f"logos/{domain_logo.name}", domain_logo) + logo_name = f"logos/{domain_logo.name}" + else: + logo_name = "https://via.placeholder.com/150?text=No+Image+Available" - webshot_logo = request.FILES.get("webshot") - webshot_logo_file = webshot_logo.name.split(".")[0] - extension = webshot_logo.name.split(".")[-1] - webshot_logo.name = webshot_logo_file[:99] + str(uuid.uuid4()) + "." + extension - default_storage.save(f"webshots/{webshot_logo.name}", webshot_logo) + if request.FILES.get("webshot"): + webshot_logo = request.FILES.get("webshot") + webshot_logo_file = webshot_logo.name.split(".")[0] + extension = webshot_logo.name.split(".")[-1] + webshot_logo.name = webshot_logo_file[:99] + str(uuid.uuid4()) + "." + extension + default_storage.save(f"webshots/{webshot_logo.name}", webshot_logo) + webshot_logo_name = f"webshots/{webshot_logo.name}" + else: + webshot_logo_name = "https://via.placeholder.com/300x200?text=No+Image+Available" + + if domain_data["facebook"] and "facebook.com" not in domain_data["facebook"]: + messages.error(request, "Facebook url should contain facebook.com") + return redirect("add_domain", id=id) + if domain_data["twitter"]: + if ( + "twitter.com" not in domain_data["twitter"] + and "x.com" not in domain_data["twitter"] + ): + messages.error(request, "Twitter url should contain twitter.com or x.com") + return redirect("add_domain", id=id) + if domain_data["github"] and "github.com" not in domain_data["github"]: + messages.error(request, "Github url should contain github.com") + return redirect("add_domain", id=id) domain_managers = User.objects.filter(email__in=managers_list, is_active=True) domain = Domain.objects.create( **domain_data, company=company_obj, - logo=f"logos/{domain_logo.name}", - webshot=f"webshots/{webshot_logo.name}", + logo=logo_name, + webshot=webshot_logo_name, + ) + + domain.managers.set(domain_managers) + domain.save() + + return redirect("company_manage_domains", id=id) + + @validate_company_user + def put(self, request, id, *args, **kwargs): + domain_id = kwargs.get("domain_id") + domain = get_object_or_404(Domain, id=domain_id) + + domain_data = { + "name": request.POST.get("domain_name", None), + "url": request.POST.get("domain_url", None), + "github": request.POST.get("github_url", None), + "twitter": request.POST.get("twitter_url", None), + "facebook": request.POST.get("facebook_url", None), + } + + if domain_data["name"] is None: + messages.error(request, "Enter domain name") + return redirect("edit_domain", id=id, domain_id=domain_id) + + if domain_data["url"] is None: + messages.error(request, "Enter domain url") + return redirect("edit_domain", id=id, domain_id=domain_id) + + parsed_url = urlparse(domain_data["url"]) + domain_name = (parsed_url.hostname).replace("www.", "") + + domain_data["name"] = domain_data["name"].lower() + + managers_list = request.POST.getlist("user") + company_obj = Company.objects.get(id=id) + + domain_exist = ( + Domain.objects.filter(Q(name=domain_data["name"]) | Q(url=domain_data["url"])) + .exclude(id=domain_id) + .exists() ) + if domain_exist: + messages.error(request, "Domain name or url already exist.") + return redirect("edit_domain", id=id, domain_id=domain_id) + + # validate domain url + try: + if is_valid_https_url(domain_data["url"]): + safe_url = rebuild_safe_url(domain_data["url"]) + try: + response = requests.get(safe_url, timeout=5, verify=False) + if response.status_code != 200: + raise Exception + except requests.exceptions.RequestException: + messages.error(request, "Domain does not exist.") + return redirect("edit_domain", id=id, domain_id=domain_id) + except ValueError: + messages.error(request, "URL validation error.") + return redirect("edit_domain", id=id, domain_id=domain_id) + + # validate domain email + user_email_domain = request.user.email.split("@")[-1] + + if not domain_name.endswith(f".{user_email_domain}") and domain_name != user_email_domain: + messages.error(request, "Your email does not match domain email. Action Denied!") + return redirect("edit_domain", id=id, domain_id=domain_id) + + for domain_manager_email in managers_list: + manager_email_domain = domain_manager_email.split("@")[-1] + if ( + not domain_name.endswith(f".{manager_email_domain}") + and domain_name != manager_email_domain + ): + messages.error( + request, f"Manager: {domain_manager_email} does not match domain email." + ) + return redirect("edit_domain", id=id, domain_id=domain_id) + + if request.FILES.get("logo"): + domain_logo = request.FILES.get("logo") + domain_logo_file = domain_logo.name.split(".")[0] + extension = domain_logo.name.split(".")[-1] + domain_logo.name = domain_logo_file[:99] + str(uuid.uuid4()) + "." + extension + default_storage.save(f"logos/{domain_logo.name}", domain_logo) + domain.logo = f"logos/{domain_logo.name}" + + if request.FILES.get("webshot"): + webshot_logo = request.FILES.get("webshot") + webshot_logo_file = webshot_logo.name.split(".")[0] + extension = webshot_logo.name.split(".")[-1] + webshot_logo.name = webshot_logo_file[:99] + str(uuid.uuid4()) + "." + extension + default_storage.save(f"webshots/{webshot_logo.name}", webshot_logo) + domain.webshot = f"webshots/{webshot_logo.name}" + + if domain_data["facebook"] and "facebook.com" not in domain_data["facebook"]: + messages.error(request, "Facebook url should contain facebook.com") + return redirect("edit_domain", id=id, domain_id=domain_id) + if domain_data["twitter"]: + if ( + "twitter.com" not in domain_data["twitter"] + and "x.com" not in domain_data["twitter"] + ): + messages.error(request, "Twitter url should contain twitter.com or x.com") + return redirect("edit_domain", id=id, domain_id=domain_id) + if domain_data["github"] and "github.com" not in domain_data["github"]: + messages.error(request, "Github url should contain github.com") + return redirect("edit_domain", id=id, domain_id=domain_id) + + domain.name = domain_data["name"] + domain.url = domain_data["url"] + domain.github = domain_data["github"] + domain.twitter = domain_data["twitter"] + domain.facebook = domain_data["facebook"] + domain.company = company_obj + domain_managers = User.objects.filter(email__in=managers_list, is_active=True) domain.managers.set(domain_managers) domain.save() - return redirect("company_manage_domains", id) + return redirect("company_manage_domains", id=id) @validate_company_user def delete(self, request, id, *args, **kwargs): - domain_id = request.GET.get("domain") + domain_id = request.POST.get("domain_id", None) domain = get_object_or_404(Domain, id=domain_id) + if domain is None: + messages.error(request, "Domain not found.") + return redirect("company_manage_domains", id=id) domain.delete() messages.success(request, "Domain deleted successfully") - return redirect("company_manage_domains", id) + return redirect("company_manage_domains", id=id) class DomainView(View): @@ -545,12 +693,12 @@ def get_current_year_monthly_reported_bar_data(self, domain_id): .order_by("month") ) - data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # count + data = [0] * 12 # Initialize a list of 12 zeros for each month for data_month in data_monthly: - data[data_month["month"]] = data_month["count"] + data[data_month["month"] - 1] = data_month["count"] - return json.dumps(data) + return data def get(self, request, pk, *args, **kwargs): domain = ( @@ -559,29 +707,30 @@ def get(self, request, pk, *args, **kwargs): "name", "url", "company__name", - "created__day", - "created__month", - "created__year", + "created", + "modified", "twitter", "facebook", "github", "logo", "webshot", + "company__id", ) .filter(id=pk) .first() ) - if domain == {}: + if not domain: raise Http404("Domain not found") - total_money_distributed = Issue.objects.filter(pk=domain["id"]).aggregate( + total_money_distributed = Issue.objects.filter(domain__id=domain["id"]).aggregate( total_money=Sum("rewarded") )["total_money"] total_money_distributed = 0 if total_money_distributed is None else total_money_distributed - total_bug_reported = Issue.objects.filter(pk=domain["id"]).count() - total_bug_accepted = Issue.objects.filter(pk=domain["id"], verified=True).count() + # Query the database for the exact domain + total_bug_reported = Issue.objects.filter(domain__id=domain["id"]).count() + total_bug_accepted = Issue.objects.filter(domain__id=domain["id"], verified=True).count() is_domain_manager = Domain.objects.filter( Q(id=domain["id"]) & Q(managers__in=[request.user]) @@ -600,12 +749,15 @@ def get(self, request, pk, *args, **kwargs): "status", "verified", "rewarded", - "created__day", - "created__month", - "created__year", + "created", ) .filter(domain__id=domain["id"]) - .order_by("-created")[:11] + .annotate( + first_screenshot=Subquery( + IssueScreenshot.objects.filter(issue_id=OuterRef("pk")).values("image")[:1] + ) + ) + .order_by("-created")[:5] ) else: latest_issues = ( @@ -621,12 +773,15 @@ def get(self, request, pk, *args, **kwargs): "status", "verified", "rewarded", - "created__day", - "created__month", - "created__year", + "created", ) .filter(domain__id=domain["id"], is_hidden=False) - .order_by("-created")[:11] + .annotate( + first_screenshot=Subquery( + IssueScreenshot.objects.filter(issue_id=OuterRef("pk")).values("image")[:1] + ) + ) + .order_by("-created")[:5] ) issue_labels = [label[-1] for label in Issue.labels] cleaned_issues = [] @@ -636,19 +791,31 @@ def get(self, request, pk, *args, **kwargs): # get top testers top_testers = ( Issue.objects.values("user__id", "user__username", "user__userprofile__user_avatar") - .filter(user__isnull=False) + .filter(domain__id=domain["id"], user__isnull=False) .annotate(count=Count("user__username")) - .order_by("-count")[:16] + .order_by("-count")[:5] ) + # Get first and last bugs + first_bug = Issue.objects.filter(domain__id=domain["id"]).order_by("created").first() + last_bug = Issue.objects.filter(domain__id=domain["id"]).order_by("-created").first() + + ongoing_bughunts = Hunt.objects.filter(domain__id=domain["id"]).annotate( + total_prize=Sum("huntprize__value") + )[:3] context = { **domain, "total_money_distributed": total_money_distributed, "total_bug_reported": total_bug_reported, "total_bug_accepted": total_bug_accepted, "latest_issues": cleaned_issues, - "monthly_activity_chart": self.get_current_year_monthly_reported_bar_data(domain["id"]), + "monthly_activity_chart": json.dumps( + self.get_current_year_monthly_reported_bar_data(domain["id"]) + ), "top_testers": top_testers, + "first_bug": first_bug, + "last_bug": last_bug, + "ongoing_bughunts": ongoing_bughunts, } return render(request, "company/view_domain.html", context)