Source code for tcms.bugs.views

# Copyright (c) 2019-2021 Alexander Todorov <atodorov@MrSenko.com>

# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html


from django.contrib.auth.decorators import permission_required
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
from django.views.generic.base import TemplateView, View
from django.views.generic.edit import CreateView, UpdateView
from guardian.decorators import permission_required as object_permission_required

from tcms.bugs.forms import BugCommentForm, NewBugForm
from tcms.bugs.models import Bug
from tcms.core.helpers.comments import add_comment
from tcms.management.models import Component


[docs] @method_decorator( object_permission_required( "bugs.view_bug", (Bug, "pk", "pk"), accept_global_perms=True ), name="dispatch", ) class Get(DetailView): model = Bug template_name = "bugs/get.html" http_method_names = ["get"]
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["comment_form"] = BugCommentForm() context["comment_form"].populate(self.object.pk) context["executions"] = self.object.executions.all() context["OBJECT_MENU_ITEMS"] = [ ( "...", [ (_("Edit"), reverse("bugs-edit", args=[self.object.pk])), ("-", "-"), ( _("Object permissions"), reverse("admin:bugs_bug_permissions", args=[self.object.pk]), ), ("-", "-"), ( _("Delete"), reverse("admin:bugs_bug_delete", args=[self.object.pk]), ), ], ) ] return context
[docs] @method_decorator(permission_required("bugs.add_bug"), name="dispatch") class New(CreateView): model = Bug form_class = NewBugForm template_name = "bugs/mutable.html"
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["page_title"] = _("New Bug") context["form_post_url"] = reverse("bugs-new") return context
[docs] def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["initial"].update( # pylint: disable=objects-update-used { "reporter": self.request.user, } ) return kwargs
[docs] def get_form(self, form_class=None): form = super().get_form(form_class) # clear fields which are set dynamically via JavaScript form.populate(self.request.POST.get("product", -1)) return form
[docs] def form_valid(self, form): response = super().form_valid(form) if not self.object.assignee: self.object.assignee = New.find_assignee(self.request.POST) self.object.save() add_comment([self.object], form.cleaned_data["text"], self.request.user) return response
[docs] @staticmethod def assignee_from_components(components): """ Return the first owner which is assigned to any of the components. This is as best as we can to automatically figure out who should be assigned to this bug. """ for component in components: if component.initial_owner: return component.initial_owner return None
[docs] @staticmethod def find_assignee(data): """ Try to automatically find an assignee for Bug by first scanning TestCase components (if present) and then components for the product. """ assignee = None if "_execution" in data: assignee = New.assignee_from_components( data["_execution"].case.component.all() ) del data["_execution"] if not assignee: assignee = New.assignee_from_components( Component.objects.filter(product=data["product"]) ) return assignee
[docs] @staticmethod def create_bug(data): """ Helper method used within Issue Tracker integration. :param data: Untrusted input, usually via HTTP request :type data: dict :return: bug :rtype: Model """ bug = None if "assignee" not in data or not data["assignee"]: data["assignee"] = New.find_assignee(data) text = data["text"] del data["text"] bug = Bug.objects.create(**data) add_comment([bug], text, bug.reporter) return bug
[docs] @method_decorator( object_permission_required( "bugs.change_bug", (Bug, "pk", "pk"), accept_global_perms=True ), name="dispatch", ) class Edit(UpdateView): model = Bug form_class = NewBugForm template_name = "bugs/mutable.html" _values_before_update = {} def _record_changes(self, new_data): result = "" for field in self.model._meta.fields: field = field.name if ( field in new_data and self._values_before_update[field] != new_data[field] ): _before_update = self._values_before_update[field] _after_update = new_data[field] result += f"{field.title()}: {_before_update} -> {_after_update}\n" if result: add_comment([self.object], result, self.request.user)
[docs] def get_initial(self): return { "assignee": self.object.assignee, }
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["page_title"] = _("Edit bug") context["form_post_url"] = reverse( "bugs-edit", args=[ self.object.pk, ], ) return context
[docs] def get_object(self, queryset=None): obj = super().get_object(queryset) for field in self.model._meta.fields: self._values_before_update[field.name] = getattr(obj, field.name) return obj
[docs] def form_valid(self, form): self._record_changes(form.cleaned_data) return super().form_valid(form)
[docs] def get_form(self, form_class=None): form = super().get_form(form_class) if self.request.POST.get("product"): form.populate(product_id=self.request.POST["product"]) else: form.populate(product_id=self.object.product_id) return form
[docs] @method_decorator(permission_required("django_comments.add_comment"), name="dispatch") class AddComment(View): http_methods = ["post"]
[docs] def post(self, request): # pylint: disable=no-self-use form = BugCommentForm(request.POST) request_action = request.POST.get("action") if form.is_valid(): bug = form.cleaned_data["bug"] if form.cleaned_data["text"]: add_comment([bug], form.cleaned_data["text"], request.user) if request_action == "close": bug.status = False add_comment([bug], _("*bug closed*"), request.user) if request_action == "reopen": bug.status = True add_comment([bug], _("*bug reopened*"), request.user) bug.save() return HttpResponseRedirect(reverse("bugs-get", args=[bug.pk])) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))