Ejemplo n.º 1
0
def test_defaults(settings):
    """todo's `defaults` module provides reasonable default values for unspecified settings.
    If a value is NOT set, it should be pulled from the hash in defaults.py.
    If a value IS set, it should be respected.
    n.b. TODO_STAFF_ONLY which defaults to True in the `defaults` module.

    модуль "defaults" todos предоставляет разумные значения по умолчанию для неуказанных настроек.
    Если значение НЕ задано, его следует извлечь из хэша в defaults.py .
    Если значение установлено, его следует принять.
    n.b. TODO_STAFF_ONLY, значение по умолчанию которого равно True в модуле "defaults".
    """

    key = "TODO_STAFF_ONLY"

    # Setting is not set, and should default to True (the value in defaults.py)
    assert not hasattr(settings, key)
    assert defaults(key)

    # Setting is already set to True and should be respected.
    settings.TODO_STAFF_ONLY = True
    assert defaults(key)

    # Setting is already set to False and should be respected.
    settings.TODO_STAFF_ONLY = False
    assert not defaults(key)
Ejemplo n.º 2
0
def add_attachment_file(request, file_data, task):
    if file_data.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
        raise Exception(_("File exceeds maximum attachment size."))

    name, extension = os.path.splitext(file_data.name)

    if extension not in defaults("TODO_LIMIT_FILE_ATTACHMENTS"):
        raise Exception(_("This site does not allow upload of '{}' files.").format(extension))

    user = request.user  # !!

    if 'django_sso_app' in settings.INSTALLED_APPS:
        user_id = user.sso_id
    else:
        user_id = user.username

    created_attachment = Attachment.objects.create(
        task=task, added_by=user, created_at=timezone.now(), file=file_data
    )

    created_attachment_task = created_attachment.task
    created_attachment_task_list = created_attachment_task.task_list

    # creating filer folders
    users_folder, _created = FilerFolder.objects.get_or_create(name='users')
    user_folder, _created = FilerFolder.objects.get_or_create(name=user_id,
                                                              parent=users_folder,
                                                              owner=user)
    user_tasks_folder, _created = FilerFolder.objects.get_or_create(name='tasks',
                                                                    parent=user_folder)
    user_tasklist_folder, _created = FilerFolder.objects.get_or_create(name=created_attachment_task_list.slug,
                                                                       parent=user_tasks_folder)
    user_tasklist_task_folder, _created = FilerFolder.objects.get_or_create(name=str(created_attachment_task.id),
                                                                            parent=user_tasklist_folder)

    # creating filer file
    filer_file = FilerFile()
    filer_file.file = created_attachment.file
    filer_file.owner = user
    filer_file.original_filename = os.path.basename(created_attachment.file.name)
    filer_file.folder = user_tasklist_task_folder

    filer_file.save()

    # update attachment
    created_attachment.filer_file = filer_file
    created_attachment.save()
Ejemplo n.º 3
0
def test_defaults(settings):
    """todo's `defaults` module provides reasonable default values for unspecified settings.
    If a value is NOT set, it should be pulled from the hash in defaults.py.
    If a value IS set, it should be respected.
    n.b. TODO_STAFF_ONLY which defaults to True in the `defaults` module."""

    key = "TODO_STAFF_ONLY"

    # Setting is not set, and should default to True (the value in defaults.py)
    assert not hasattr(settings, key)
    assert defaults(key)

    # Setting is already set to True and should be respected.
    settings.TODO_STAFF_ONLY = True
    assert defaults(key)

    # Setting is already set to False and should be respected.
    settings.TODO_STAFF_ONLY = False
    assert not defaults(key)
Ejemplo n.º 4
0
def staff_check(user):
    """If TODO_STAFF_ONLY is set to True, limit view access to staff users only.
        # FIXME: More granular access control needed - see
        https://github.com/shacker/django-todo/issues/50
    """

    if defaults("TODO_STAFF_ONLY"):
        return user.is_staff
    else:
        # If unset or False, allow all logged in users
        return True
Ejemplo n.º 5
0
def book_edit(request, book_id: int) -> HttpResponse:
    published_book = get_object_or_404(PublishedBook, pk=book_id)

    if request.POST:
        form = PublishedBookForm(request.POST)

        if form.is_valid():
            # Handle uploaded files
            if request.FILES.get("attachment_file_input"):
                file = request.FILES.get("attachment_file_input")

                if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
                    messages.error(
                        request,
                        f"El archivo excede el tamaño máximo permitido.")
                    return redirect(request.path)

                published_book.final_version = file

            if request.FILES.get("attachment_image_input"):
                published_book.related_image = request.FILES.get(
                    "attachment_image_input")

            published_book.title = request.POST.get("title")
            published_book.book.description = request.POST.get("description")
            published_book.author_text = request.POST.get("author")
            published_book.book.completed = True
            published_book.book.save()
            published_book.save()
            if can_covert_to_epub():
                published_book.final_version_epub = convert_to_epub(
                    published_book.final_version.path)
                published_book.save()

            messages.success(
                request,
                "¡Perfecto! Los parámetros se han modificado correctamente.")

        else:
            print(form.errors)
            messages.error(request,
                           "Lo sentimos, el libro no ha podido editarse.")
            return redirect(request.path)

        return redirect("todo:book_detail", book_id=published_book.book.id)

    context = {'book': published_book, 'edit_book_form': PublishedBookForm()}

    return render(request, "todo/book_edit.html", context)
Ejemplo n.º 6
0
def staff_check(user):
    """If TODO_STAFF_ONLY is set to True, limit view access to staff users only.
        # FIXME: More granular access control needed - see
        https://github.com/shacker/django-todo/issues/50

        Если для параметра DO_STUFF_ONLY установлено значение True, ограничьте доступ к просмотру только для сотрудников.
        FIXME: Требуется более детальный контроль доступа
    """

    if defaults("TODO_STAFF_ONLY"):
        return user.is_staff
    else:
        # If unset or False, allow all logged in users
        # Если значение не задано или равно False, разрешить всем зарегистрированным пользователям
        return True
Ejemplo n.º 7
0
def external_add(request) -> HttpResponse:
    """Allow authenticated users who don't have access to the rest of the ticket system to file a ticket
    in the list specified in settings (e.g. django-todo can be used a ticket filing system for a school, where
    students can file tickets without access to the rest of the todo system).

    Publicly filed tickets are unassigned unless settings.DEFAULT_ASSIGNEE exists.
    """

    if not settings.TODO_DEFAULT_LIST_SLUG:
        # We do NOT provide a default in defaults
        raise RuntimeError(
            "This feature requires TODO_DEFAULT_LIST_SLUG: in settings. See documentation."
        )

    if not TaskList.objects.filter(
            slug=settings.TODO_DEFAULT_LIST_SLUG).exists():
        raise RuntimeError(
            "There is no TaskList with slug specified for TODO_DEFAULT_LIST_SLUG in settings."
        )

    if request.POST:
        form = AddExternalTaskForm(request.POST)

        if form.is_valid():
            current_site = Site.objects.get_current()
            task = form.save(commit=False)
            task.task_list = TaskList.objects.get(
                slug=settings.TODO_DEFAULT_LIST_SLUG)
            task.created_by = request.user
            if defaults("TODO_DEFAULT_ASSIGNEE"):
                task.assigned_to = get_user_model().objects.get(
                    username=settings.TODO_DEFAULT_ASSIGNEE)
            task.save()

            # Send email to assignee if we have one
            if task.assigned_to:
                email_subject = render_to_string(
                    "todo/email/assigned_subject.txt", {"task": task.title})
                email_body = render_to_string("todo/email/assigned_body.txt", {
                    "task": task,
                    "site": current_site
                })
                try:
                    send_mail(
                        email_subject,
                        email_body,
                        task.created_by.email,
                        [task.assigned_to.email],
                        fail_silently=False,
                    )
                except ConnectionRefusedError:
                    messages.warning(
                        request,
                        _("Task saved but mail not sent. Contact your administrator."
                          ))

            messages.success(
                request,
                _("Your trouble ticket has been submitted. We'll get back to you soon."
                  ))
            return redirect(defaults("TODO_PUBLIC_SUBMIT_REDIRECT"))

    else:
        form = AddExternalTaskForm(initial={"priority": 999})

    context = {"form": form}

    return render(request, "todo/add_task_external.html", context)
Ejemplo n.º 8
0
def external_add(request) -> HttpResponse:
    """Allow authenticated users who don't have access to the rest of the ticket system to file a ticket
    in the list specified in settings (e.g. django-todo can be used a ticket filing system for a school, where
    students can file tickets without access to the rest of the todo system).

    Publicly filed tickets are unassigned unless settings.DEFAULT_ASSIGNEE exists.

    Разрешить аутентифицированным пользователям, у которых нет доступа к остальной части системы регистрации,
    подавать заявки в список, указанный в настройках (например, django-todo может использоваться в системе регистрации
    заявок для школы, где учащиеся могут подавать заявки без доступа к остальной части системы регистрации заявок).

    Общедоступные заявки не назначаются, если только не прописана settings.DEFAULT_ASSIGNEE.
    """

    if not settings.TODO_DEFAULT_LIST_SLUG:
        # We do NOT provide a default in defaults
        # Мы не предоставляем дефолт по умолчанию
        raise RuntimeError(
            "This feature requires TODO_DEFAULT_LIST_SLUG: in settings. See documentation."
        )

    if not TaskList.objects.filter(slug=settings.TODO_DEFAULT_LIST_SLUG).exists():
        raise RuntimeError(
            "There is no TaskList with slug specified for TODO_DEFAULT_LIST_SLUG in settings."
        )

    if request.POST:
        form = AddExternalTaskForm(request.POST)

        if form.is_valid():
            current_site = Site.objects.get_current()
            task = form.save(commit=False)
            task.task_list = TaskList.objects.get(slug=settings.TODO_DEFAULT_LIST_SLUG)
            task.created_by = request.user
            if defaults("TODO_DEFAULT_ASSIGNEE"):
                task.assigned_to = get_user_model().objects.get(username=settings.TODO_DEFAULT_ASSIGNEE)
            task.save()

            # Send email to assignee if we have one
            # Отправьте электронное письмо правопреемнику, если он у нас есть
            if task.assigned_to:
                email_subject = render_to_string(
                    "todo/email/assigned_subject.txt", {"task": task.title}
                )
                email_body = render_to_string(
                    "todo/email/assigned_body.txt", {"task": task, "site": current_site}
                )
                try:
                    send_mail(
                        email_subject,
                        email_body,
                        task.created_by.email,
                        [task.assigned_to.email],
                        fail_silently=False,
                    )
                except ConnectionRefusedError:
                    messages.warning(
                        request, "Task saved but mail not sent. Contact your administrator."
                    )

            messages.success(
                request, "Your trouble ticket has been submitted. We'll get back to you soon."
            )
            return redirect(defaults("TODO_PUBLIC_SUBMIT_REDIRECT"))

    else:
        form = AddExternalTaskForm(initial={"priority": 999})

    context = {"form": form}

    return render(request, "todo/add_task_external.html", context)
Ejemplo n.º 9
0
def book_publish(request, book_id: int) -> HttpResponse:
    book = get_object_or_404(Book, pk=book_id)
    user_email = request.user
    editor = Editor.objects.filter(user=user_email).first()

    if editor == None:
        raise PermissionDenied
        
    if (book.editor != editor) and (not editor.chief):
        raise PermissionDenied
    
    if book.completed:
        raise PermissionDenied

    if request.POST:
        form = PublishedBookForm(request.POST)

        if form.is_valid():
            published_book = form.save(commit=False)     
            published_book.book = book

            # Handle uploaded files
            if request.FILES.get("attachment_file_input"):
                file = request.FILES.get("attachment_file_input")

                if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
                    messages.error(request, f"El archivo excede el tamaño máximo permitido.")
                    return redirect(request.path)

                published_book.final_version = file

            else:
                messages.error(request, f"Debes adjuntar la versión maquetada final en pdf, sino no podremos proceder a su publicación.")
                return redirect(request.path)
            
            if request.FILES.get("attachment_image_input"):
                published_book.related_image = request.FILES.get("attachment_image_input")

            published_book.title = request.POST.get("title")
            published_book.book.description = request.POST.get("description")
            published_book.author_text = request.POST.get("author")
            published_book.book.completed = True
            published_book.book.save()
            published_book.save()
            try_add_epub_version(published_book)

            messages.success(request, "¡Enhorabuena! El libro ha sido publicado correctamente.")
            
            ######### Send email to author
            email_body = render_to_string(
                "todo/email/published_book.txt", {"book": book}
            )

            send_mail(
                "Libro publicado",
                email_body,
                None,
                [book.author.user.email],
                fail_silently=False,
            )
        else:
            print(form.errors)
            messages.error(request, "Lo sentimos, el libro no ha podido publicarse.")
            return redirect(request.path)
            
        return redirect("todo:book_detail", book_id=book.id)
    
    context = {
        'book': book,
        'publish_form': PublishedBookForm()
    }
    return render(request, "todo/book_publish.html", context)
Ejemplo n.º 10
0
def external_add(request) -> HttpResponse:

    if request.POST:
        form = AddExternalBookForm(request.POST)
        form_book = AddBookForm(request.POST)
        
        if form.is_valid() and form_book.is_valid():
            current_site = Site.objects.get_current()
            User = get_user_model()
            email = request.POST['email']
            user = User.objects.filter(email=email).first()
            
            if not user:
                password = User.objects.make_random_password()
                user = User.objects.create_user(email, password)
            user_info = UserInfo.objects.filter(user=user).first()
            if not user_info:
                user_info = form.save(commit=False)
                user_info.user = user
                user_info.save()
            writer, created_writer = Writer.objects.update_or_create(user=user)


            book = form_book.save(commit=False)

            # Handle uploaded files
            if request.FILES.get("attachment_file_input"):
                file = request.FILES.get("attachment_file_input")

                if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
                    messages.error(request, f"El archivo excede el tamaño máximo permitido")
                    return redirect("todo:external_add")

                name, extension = os.path.splitext(file.name)

                if extension not in defaults("TODO_LIMIT_FILE_ATTACHMENTS"):
                    messages.error(request, f"Este sitio no eccepta atchivos de extensión {extension}")
                    return redirect("todo:external_add")

                book.file = file

            else:
                messages.error(request, f"Debes adjuntar un libro.")
                return redirect("todo:external_add")

            book.author = writer
            book.save()


            messages.success(
                request, "Su libro se ha enviado. Nos pondremos en contacto con usted pronto."
            )

            send_emails(book, user_info, created_writer, email)

            return redirect(defaults("TODO_PUBLIC_SUBMIT_REDIRECT"))


    else:
        form = AddExternalBookForm(initial={"priority": 999})
        form_book = AddBookForm()

    context = {"form": form, "form_book": form_book}

    return render(request, "todo/add_task_external.html", context)
Ejemplo n.º 11
0
def task_detail(request, task_id: int) -> HttpResponse:
    """View task details. Allow task details to be edited. Process new comments on task.
    """

    task = get_object_or_404(Task, pk=task_id)
    comment_list = Comment.objects.filter(task=task_id).order_by("-date")

    # Ensure user has permission to view task. Superusers can view all tasks.
    # Get the group this task belongs to, and check whether current user is a member of that group.
    if not user_can_read_task(task, request.user):
        raise PermissionDenied

    # Handle task merging
    if not HAS_TASK_MERGE:
        merge_form = None
    else:

        class MergeForm(forms.Form):
            merge_target = forms.ModelChoiceField(
                queryset=Task.objects.all(),
                widget=autocomplete.ModelSelect2(
                    url=reverse("todo:task_autocomplete", kwargs={"task_id": task_id})
                ),
            )

        # Handle task merging
        if not request.POST.get("merge_task_into"):
            merge_form = MergeForm()
        else:
            merge_form = MergeForm(request.POST)
            if merge_form.is_valid():
                merge_target = merge_form.cleaned_data["merge_target"]
            if not user_can_read_task(merge_target, request.user):
                raise PermissionDenied

            task.merge_into(merge_target)
            return redirect(reverse("todo:task_detail", kwargs={"task_id": merge_target.pk}))

    # Save submitted comments
    handle_add_comment(request, task)

    # Save task edits
    if not request.POST.get("add_edit_task"):
        form = AddEditTaskForm(request.user, instance=task, initial={"task_list": task.task_list})
    else:
        form = AddEditTaskForm(
            request.user, request.POST, instance=task, initial={"task_list": task.task_list}
        )

        if form.is_valid():
            item = form.save(commit=False)
            item.note = bleach.clean(form.cleaned_data["note"], strip=True)
            item.save()
            messages.success(request, "The task has been edited.")
            return redirect(
                "todo:list_detail", list_id=task.task_list.id, list_slug=task.task_list.slug
            )

    # Mark complete
    if request.POST.get("toggle_done"):
        results_changed = toggle_task_completed(task.id)
        if results_changed:
            messages.success(request, f"Changed completion status for task {task.id}")

        return redirect("todo:task_detail", task_id=task.id)

    if task.due_date:
        thedate = task.due_date
    else:
        thedate = datetime.datetime.now()

    # Handle uploaded files
    if request.FILES.get("attachment_file_input"):
        file = request.FILES.get("attachment_file_input")

        if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
            messages.error(request, f"File exceeds maximum attachment size.")
            return redirect("todo:task_detail", task_id=task.id)

        name, extension = os.path.splitext(file.name)

        if extension not in defaults("TODO_LIMIT_FILE_ATTACHMENTS"):
            messages.error(request, f"This site does not allow upload of {extension} files.")
            return redirect("todo:task_detail", task_id=task.id)

        Attachment.objects.create(
            task=task, added_by=request.user, timestamp=datetime.datetime.now(), file=file
        )
        messages.success(request, f"File attached successfully")
        return redirect("todo:task_detail", task_id=task.id)

    context = {
        "task": task,
        "comment_list": comment_list,
        "form": form,
        "merge_form": merge_form,
        "thedate": thedate,
        "comment_classes": defaults("TODO_COMMENT_CLASSES"),
        "attachments_enabled": defaults("TODO_ALLOW_FILE_ATTACHMENTS"),
    }

    return render(request, "todo/task_detail.html", context)
Ejemplo n.º 12
0
def task_detail(request, task_id: int) -> HttpResponse:
    """View task details. Allow task details to be edited. Process new comments on task.
    """

    task = get_object_or_404(Task, pk=task_id)
    # comment_list = Comment.objects.filter(task=task_id).order_by("-date")  # pai

    # Ensure user has permission to view task. Superusers can view all tasks.
    # Get the group this task belongs to, and check whether current user is a member of that group.
    if not user_can_read_task(task, request.user):
        # raise PermissionDenied  # pai
        raise Http404

    comment_list = Comment.objects.filter(task=task_id).order_by("-created_at")  # pai

    # Handle task merging
    if not HAS_TASK_MERGE:
        merge_form = None
    else:

        class MergeForm(forms.Form):
            merge_target = forms.ModelChoiceField(
                queryset=Task.objects.filter(is_active=True),
                widget=autocomplete.ModelSelect2(
                    url=reverse("todo:task_autocomplete", kwargs={"task_id": task_id})
                ),
            )

        # Handle task merging
        if not request.POST.get("merge_task_into"):
            merge_form = MergeForm()
        else:
            merge_form = MergeForm(request.POST)
            if merge_form.is_valid():
                merge_target = merge_form.cleaned_data["merge_target"]
            if not user_can_read_task(merge_target, request.user):
                raise PermissionDenied

            task.merge_into(merge_target)
            return redirect(reverse("todo:task_detail", kwargs={"task_id": merge_target.pk}))

    # Save submitted comments
    handle_add_comment(request, task)

    # Save task edits
    # staff can edit task, non staff can only if creator
    if (task.procedure_uuid is None and staff_check(request.user) or
            (task.procedure_uuid is None and task.created_by == request.user)) \
       and request.POST.getlist("add_edit_task"):  # pai

        form = AddEditTaskForm(
            request.user, request.POST, instance=task, initial={"task_list": task.task_list}
        )

        if form.is_valid():
            item = form.save(commit=False)
            item.note = bleach.clean(form.cleaned_data["note"], strip=True)
            item.title = bleach.clean(form.cleaned_data["title"], strip=True)
            item.save()
            messages.success(request, _("The task has been edited."))
            return redirect(
                "todo:list_detail", list_id=task.task_list.id, list_slug=task.task_list.slug
            )
    else:
        form = AddEditTaskForm(request.user, instance=task, initial={"task_list": task.task_list})

    """
    # pai (why duplicate toggle_done view?)
    # Mark complete
    if request.POST.get("toggle_done"):
        results_changed = toggle_task_completed(task.id, user=request.user)
        if results_changed:
            messages.success(request, _("Task completion status changed for ") + '"' + task.title + '".')
        else:
            messages.error(request, _("Can not change completion status for ") + '"' + task.title + '".')

        return redirect("todo:task_detail", task_id=task.id)
    """

    if task.due_date:
        thedate = task.due_date
    else:
        thedate = timezone.now()

    # Handle uploaded files
    # pai
    # Attachment.objects.create(
    #    task=task, added_by=request.user, timestamp=datetime.datetime.now(), file=file
    # )

    attachment_file = request.FILES.get("attachment_file_input", None)
    if attachment_file is not None:
        try:
            add_attachment_file(request, attachment_file, task)  # pai

        except Exception as e:
            messages.error(request, str(e))
            return redirect("todo:task_detail", task_id=task.id)

        messages.success(request, f"File attached successfully")
        return redirect("todo:task_detail", task_id=task.id)

    context = {
        "task": task,
        "comment_list": comment_list,
        "form": form,
        "merge_form": merge_form,
        "thedate": thedate,
        "comment_classes": defaults("TODO_COMMENT_CLASSES"),
        "attachments_enabled": defaults("TODO_ALLOW_FILE_ATTACHMENTS"),
    }

    return render(request, "todo/task_detail.html", context)
Ejemplo n.º 13
0
def external_add(request) -> HttpResponse:
    """Allow authenticated users who don't have access to the rest of the ticket system to file a ticket
    in the list specified in settings (e.g. django-todo can be used a ticket filing system for a school, where
    students can file tickets without access to the rest of the todo system).

    Publicly filed tickets are unassigned unless settings.DEFAULT_ASSIGNEE exists.
    """

    if not settings.TODO_DEFAULT_LIST_SLUG:
        # We do NOT provide a default in defaults
        raise RuntimeError(
            "This feature requires TODO_DEFAULT_LIST_SLUG: in settings. See documentation."
        )

    if not TaskList.objects.filter(slug=settings.TODO_DEFAULT_LIST_SLUG).exists():
        raise RuntimeError(
            "There is no TaskList with slug specified for TODO_DEFAULT_LIST_SLUG in settings."
        )

    if request.POST:
        form = AddExternalTaskForm(request.POST)

        if form.is_valid():
            current_site = Site.objects.get_current()
            task = form.save(commit=False)
            task.task_list = TaskList.objects.get(slug=settings.TODO_DEFAULT_LIST_SLUG)
            task.created_by = request.user
            if defaults("TODO_DEFAULT_ASSIGNEE"):
                task.assigned_to = get_user_model().objects.get(username=settings.TODO_DEFAULT_ASSIGNEE)
            task.save()

            # Send email to assignee if we have one
            if task.assigned_to:
                email_subject = render_to_string(
                    "todo/email/assigned_subject.txt", {"task": task.title}
                )
                email_body = render_to_string(
                    "todo/email/assigned_body.txt", {"task": task, "site": current_site}
                )
                try:
                    send_mail(
                        email_subject,
                        email_body,
                        task.created_by.email,
                        [task.assigned_to.email],
                        fail_silently=False,
                    )
                except ConnectionRefusedError:
                    messages.warning(
                        request, "Task saved but mail not sent. Contact your administrator."
                    )

            messages.success(
                request, "Your trouble ticket has been submitted. We'll get back to you soon."
            )
            return redirect(defaults("TODO_PUBLIC_SUBMIT_REDIRECT"))

    else:
        form = AddExternalTaskForm(initial={"priority": 999})

    context = {"form": form}

    return render(request, "todo/add_task_external.html", context)
Ejemplo n.º 14
0
def task_detail(request, task_id: int) -> HttpResponse:
    """View task details. Allow task details to be edited. Process new comments on task.

    Просмотр сведений о задаче. Разрешить редактирование сведений о задаче. Обработайте новые комментарии к задаче.
    """

    task = get_object_or_404(Task, pk=task_id)
    comment_list = Comment.objects.filter(task=task_id).order_by("-date")

    # Ensure user has permission to view task. Superusers can view all tasks.
    # Убедитесь, что у пользователя есть разрешение на просмотр задачи. Суперпользователи могут просматривать все задачи.

    # Get the group this task belongs to, and check whether current user is a member of that group.
    # Получите группу, к которой принадлежит эта задача, и проверьте, является ли текущий пользователь членом этой группы.
    if not user_can_read_task(task, request.user):
        raise PermissionDenied

    # Handle task merging
    # Обрабатывать слияние задач (скрыто)
    if not HAS_TASK_MERGE:
        merge_form = None
    else:

        class MergeForm(forms.Form):
            merge_target = forms.ModelChoiceField(
                queryset=Task.objects.all(),
                widget=autocomplete.ModelSelect2(
                    url=reverse("todo:task_autocomplete", kwargs={"task_id": task_id})
                ),
            )

        # Handle task merging
        # Обрабатывать слияние задач (скрыто)
        if not request.POST.get("merge_task_into"):
            merge_form = MergeForm()
        else:
            merge_form = MergeForm(request.POST)
            if merge_form.is_valid():
                merge_target = merge_form.cleaned_data["merge_target"]
            if not user_can_read_task(merge_target, request.user):
                raise PermissionDenied

            task.merge_into(merge_target)
            return redirect(reverse("todo:task_detail", kwargs={"task_id": merge_target.pk}))

    # Save submitted comments
    # Сохранить отправленные комментарии
    handle_add_comment(request, task)

    # Save task edits
    # Сохранение изменений задачи
    if not request.POST.get("add_edit_task"):
        form = AddEditTaskForm(request.user, instance=task, initial={"task_list": task.task_list})
    else:
        form = AddEditTaskForm(
            request.user, request.POST, instance=task, initial={"task_list": task.task_list}
        )

        if form.is_valid():
            item = form.save(commit=False)
            item.note = bleach.clean(form.cleaned_data["note"], strip=True)
            item.title = bleach.clean(form.cleaned_data["title"], strip=True)
            item.save()
            messages.success(request, "The task has been edited.")
            return redirect(
                "todo:list_detail", list_id=task.task_list.id, list_slug=task.task_list.slug
            )

    # Mark complete
    # Отметить завершение
    if request.POST.get("toggle_done"):
        results_changed = toggle_task_completed(task.id)
        if results_changed:
            messages.success(request, f"Changed completion status for task {task.id}")

        return redirect("todo:task_detail", task_id=task.id)

    if task.due_date:
        thedate = task.due_date
    else:
        thedate = datetime.datetime.now()

    # Handle uploaded files
    # Обрабатывать загруженные файлы
    if request.FILES.get("attachment_file_input"):
        file = request.FILES.get("attachment_file_input")

        if file.size > defaults("TODO_MAXIMUM_ATTACHMENT_SIZE"):
            messages.error(request, f"File exceeds maximum attachment size.")
            return redirect("todo:task_detail", task_id=task.id)

        name, extension = os.path.splitext(file.name)

        if extension not in defaults("TODO_LIMIT_FILE_ATTACHMENTS"):
            messages.error(request, f"This site does not allow upload of {extension} files.")
            return redirect("todo:task_detail", task_id=task.id)

        Attachment.objects.create(
            task=task, added_by=request.user, timestamp=datetime.datetime.now(), file=file
        )
        messages.success(request, f"File attached successfully")
        return redirect("todo:task_detail", task_id=task.id)

    context = {
        "task": task,
        "comment_list": comment_list,
        "form": form,
        "merge_form": merge_form,
        "thedate": thedate,
        "comment_classes": defaults("TODO_COMMENT_CLASSES"),
        "attachments_enabled": defaults("TODO_ALLOW_FILE_ATTACHMENTS"),
    }

    return render(request, "todo/task_detail.html", context)