• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rafalp / Misago / 9487666026

12 Jun 2024 06:16PM UTC coverage: 97.699% (-1.0%) from 98.716%
9487666026

push

github

web-flow
Replace forum options with account settings (#1742)

1947 of 1979 new or added lines in 68 files covered. (98.38%)

661 existing lines in 143 files now uncovered.

52601 of 53840 relevant lines covered (97.7%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.38
/misago/threads/viewmodels/thread.py
1
from django.shortcuts import get_object_or_404
1✔
2
from django.utils.translation import pgettext
1✔
3

4
from ...acl.objectacl import add_acl_to_obj
1✔
5
from ...categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
1✔
6
from ...categories.models import Category
1✔
7
from ...core.shortcuts import validate_slug
1✔
8
from ...core.viewmodel import ViewModel as BaseViewModel
1✔
9
from ...notifications.models import WatchedThread
1✔
10
from ...notifications.threads import get_watched_thread
1✔
11
from ...readtracker.threadstracker import make_read_aware
1✔
12
from ..models import Poll, Thread
1✔
13
from ..participants import make_participants_aware
1✔
14
from ..permissions import (
1✔
15
    allow_see_private_thread,
16
    allow_see_thread,
17
    allow_use_private_threads,
18
)
19
from ..serializers import PrivateThreadSerializer, ThreadSerializer
1✔
20
from ..threadtypes import trees_map
1✔
21

22
__all__ = ["ForumThread", "PrivateThread"]
1✔
23

24
BASE_RELATIONS = [
1✔
25
    "category",
26
    "poll",
27
    "starter",
28
    "starter__rank",
29
    "starter__ban_cache",
30
    "starter__online_tracker",
31
]
32

33

34
class ViewModel(BaseViewModel):
1✔
35
    def __init__(
1✔
36
        self,
37
        request,
38
        pk,
39
        slug=None,
40
        path_aware=False,
41
        read_aware=False,
42
        watch_aware=False,
43
        poll_votes_aware=False,
44
    ):
45
        self.request = request
1✔
46

47
        model = self.get_thread(request, pk, slug)
1✔
48

49
        if path_aware:
1✔
50
            model.path = self.get_thread_path(model.category)
1✔
51

52
        add_acl_to_obj(request.user_acl, model.category)
1✔
53
        add_acl_to_obj(request.user_acl, model)
1✔
54

55
        if read_aware:
1✔
56
            make_read_aware(request, model)
1✔
57

58
        if watch_aware and request.user.is_authenticated:
1✔
59
            self._watched_thread = get_watched_thread(request.user, model)
1✔
60
        else:
61
            self._watched_thread = None
1✔
62

63
        self._model = model
1✔
64

65
        try:
1✔
66
            self._poll = model.poll
1✔
67
            add_acl_to_obj(request.user_acl, self._poll)
1✔
68

69
            if poll_votes_aware:
1✔
70
                self._poll.make_choices_votes_aware(request.user)
1✔
71
        except Poll.DoesNotExist:
1✔
72
            self._poll = None
1✔
73

74
    @property
1✔
75
    def watched_thread(self) -> WatchedThread | None:
1✔
76
        return self._watched_thread
1✔
77

78
    @property
1✔
79
    def poll(self):
1✔
UNCOV
80
        return self._poll
×
81

82
    def get_thread(self, request, pk, slug=None):
1✔
UNCOV
83
        raise NotImplementedError(
×
84
            "Thread view model has to implement get_thread(request, pk, slug=None)"
85
        )
86

87
    def get_thread_path(self, category):
1✔
88
        thread_path = []
1✔
89

90
        if category.level:
1✔
91
            categories = Category.objects.filter(
1✔
92
                tree_id=category.tree_id, lft__lte=category.lft, rght__gte=category.rght
93
            ).order_by("level")
94
            thread_path = list(categories)
1✔
95
        else:
96
            thread_path = [category]
1✔
97

98
        thread_path[0].name = self.get_root_name()
1✔
99
        return thread_path
1✔
100

101
    def get_root_name(self):
1✔
UNCOV
102
        raise NotImplementedError("Thread view model has to implement get_root_name()")
×
103

104
    def get_frontend_context(self):
1✔
UNCOV
105
        raise NotImplementedError(
×
106
            "Thread view model has to implement get_frontend_context()"
107
        )
108

109
    def get_template_context(self):
1✔
110
        thread_notifications = None
1✔
111
        if self._watched_thread:
1✔
112
            if self._watched_thread.send_emails:
1✔
113
                thread_notifications = "SITE_AND_EMAIL"
1✔
114
            else:
UNCOV
115
                thread_notifications = "SITE_ONLY"
×
116

117
        return {
1✔
118
            "thread": self._model,
119
            "thread_notifications": thread_notifications,
120
            "poll": self._poll,
121
            "category": self._model.category,
122
            "breadcrumbs": self._model.path,
123
        }
124

125

126
class ForumThread(ViewModel):
1✔
127
    def get_thread(self, request, pk, slug=None):
1✔
128
        thread = get_object_or_404(
1✔
129
            Thread.objects.select_related(*BASE_RELATIONS),
130
            pk=pk,
131
            category__tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME),
132
        )
133

134
        allow_see_thread(request.user_acl, thread)
1✔
135
        if slug:
1✔
136
            validate_slug(thread, slug)
1✔
137
        return thread
1✔
138

139
    def get_root_name(self):
1✔
140
        return pgettext("threads root name", "Threads")
1✔
141

142
    def get_frontend_context(self):
1✔
143
        return ThreadSerializer(
1✔
144
            self._model,
145
            context={
146
                "settings": self.request.settings,
147
                "watched_thread": self._watched_thread,
148
            },
149
        ).data
150

151

152
class PrivateThread(ViewModel):
1✔
153
    def get_thread(self, request, pk, slug=None):
1✔
154
        allow_use_private_threads(request.user_acl)
1✔
155

156
        thread = get_object_or_404(
1✔
157
            Thread.objects.select_related(*BASE_RELATIONS),
158
            pk=pk,
159
            category__tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT_NAME),
160
        )
161

162
        make_participants_aware(request.user, thread)
1✔
163
        allow_see_private_thread(request.user_acl, thread)
1✔
164

165
        if slug:
1✔
166
            validate_slug(thread, slug)
1✔
167

168
        return thread
1✔
169

170
    def get_root_name(self):
1✔
171
        return pgettext("private threads root name", "Private threads")
1✔
172

173
    def get_frontend_context(self):
1✔
174
        return PrivateThreadSerializer(
1✔
175
            self._model,
176
            context={
177
                "settings": self.request.settings,
178
                "watched_thread": self._watched_thread,
179
            },
180
        ).data
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc