punkweb-bb 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- punkweb_bb/__pycache__/models.cpython-311.pyc +0 -0
- punkweb_bb/__pycache__/settings.cpython-311.pyc +0 -0
- punkweb_bb/__pycache__/tests.cpython-311.pyc +0 -0
- punkweb_bb/__pycache__/views.cpython-311.pyc +0 -0
- punkweb_bb/migrations/0002_thread_view_count.py +18 -0
- punkweb_bb/migrations/__pycache__/0002_thread_view_count.cpython-311.pyc +0 -0
- punkweb_bb/models.py +4 -0
- punkweb_bb/settings.py +1 -0
- punkweb_bb/static/punkweb_bb/css/profile.css +7 -0
- punkweb_bb/templates/punkweb_bb/base.html +1 -1
- punkweb_bb/templates/punkweb_bb/subcategory.html +7 -1
- punkweb_bb/templates/punkweb_bb/thread.html +5 -2
- punkweb_bb/templates/punkweb_bb/thread_create.html +3 -0
- punkweb_bb/templates/punkweb_bb/thread_update.html +3 -0
- punkweb_bb/templatetags/__pycache__/__init__.cpython-311.pyc +0 -0
- punkweb_bb/templatetags/__pycache__/humanize_count.cpython-311.pyc +0 -0
- punkweb_bb/templatetags/__pycache__/humanize_int.cpython-311.pyc +0 -0
- punkweb_bb/templatetags/humanize_int.py +12 -0
- punkweb_bb/tests.py +518 -2
- punkweb_bb/views.py +12 -3
- punkweb_bb-0.1.3.dist-info/METADATA +171 -0
- {punkweb_bb-0.1.2.dist-info → punkweb_bb-0.1.3.dist-info}/RECORD +25 -19
- punkweb_bb-0.1.2.dist-info/METADATA +0 -72
- {punkweb_bb-0.1.2.dist-info → punkweb_bb-0.1.3.dist-info}/LICENSE +0 -0
- {punkweb_bb-0.1.2.dist-info → punkweb_bb-0.1.3.dist-info}/WHEEL +0 -0
- {punkweb_bb-0.1.2.dist-info → punkweb_bb-0.1.3.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-07 04:24
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("punkweb_bb", "0001_initial"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name="thread",
|
|
15
|
+
name="view_count",
|
|
16
|
+
field=models.PositiveIntegerField(default=0),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
Binary file
|
punkweb_bb/models.py
CHANGED
|
@@ -5,6 +5,7 @@ import os
|
|
|
5
5
|
from django.contrib.auth import get_user_model
|
|
6
6
|
from django.core.cache import cache
|
|
7
7
|
from django.db import models
|
|
8
|
+
from django.forms import ValidationError
|
|
8
9
|
from django.urls import reverse
|
|
9
10
|
from django.utils import timezone
|
|
10
11
|
from precise_bbcode.fields import BBCodeTextField
|
|
@@ -107,6 +108,7 @@ class Thread(UUIDPrimaryKeyMixin, TimestampMixin):
|
|
|
107
108
|
is_pinned = models.BooleanField(default=False)
|
|
108
109
|
is_closed = models.BooleanField(default=False)
|
|
109
110
|
last_post_created_at = models.DateTimeField(auto_now_add=True)
|
|
111
|
+
view_count = models.PositiveIntegerField(default=0)
|
|
110
112
|
|
|
111
113
|
class Meta:
|
|
112
114
|
ordering = (
|
|
@@ -161,6 +163,8 @@ class Post(UUIDPrimaryKeyMixin, TimestampMixin):
|
|
|
161
163
|
return thread_url
|
|
162
164
|
|
|
163
165
|
def save(self, *args, **kwargs):
|
|
166
|
+
if self.thread.is_closed:
|
|
167
|
+
raise ValidationError("Cannot add posts to a closed thread.")
|
|
164
168
|
if self._state.adding:
|
|
165
169
|
self.thread.last_post_created_at = timezone.now()
|
|
166
170
|
self.thread.save()
|
punkweb_bb/settings.py
CHANGED
|
@@ -4,4 +4,5 @@ PUNKWEB_BB = getattr(settings, "PUNKWEB_BB", {})
|
|
|
4
4
|
|
|
5
5
|
SITE_NAME = PUNKWEB_BB.get("SITE_NAME", "PUNKWEB")
|
|
6
6
|
SITE_TITLE = PUNKWEB_BB.get("SITE_TITLE", "PunkwebBB")
|
|
7
|
+
FAVICON = PUNKWEB_BB.get("FAVICON", "punkweb_bb/favicon.ico")
|
|
7
8
|
SHOUTBOX_ENABLED = PUNKWEB_BB.get("SHOUTBOX_ENABLED", True)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta charset="UTF-8" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>{% block title_prefix %}{% endblock %}{{ punkweb_bb.settings.SITE_TITLE|default:"PunkwebBB" }}</title>
|
|
9
|
-
<link rel="icon" href="{% static
|
|
9
|
+
<link rel="icon" href="{% static punkweb_bb.settings.FAVICON %}" />
|
|
10
10
|
<link rel="stylesheet" href="{% static 'punkweb_bb/vendor/open-color.css' %}" />
|
|
11
11
|
<link rel="stylesheet" href="{% static 'punkweb_bb/vendor/prism.css' %}" />
|
|
12
12
|
<link rel="stylesheet" href="{% static 'punkweb_bb/css/defaults.css' %}" />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% extends 'punkweb_bb/base.html' %}
|
|
2
|
-
{% load static %}
|
|
2
|
+
{% load static humanize_int %}
|
|
3
3
|
|
|
4
4
|
{% block title_prefix %}{{subcategory.name}} | {% endblock%}
|
|
5
5
|
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
<li class="pw-breadcrumb-item">
|
|
15
15
|
<a href="{% url 'punkweb_bb:index' %}">Home</a>
|
|
16
16
|
</li>
|
|
17
|
+
<li class="pw-breadcrumb-item">
|
|
18
|
+
<a href="{{subcategory.category.get_absolute_url}}">{{subcategory.category.name}}</a>
|
|
19
|
+
</li>
|
|
17
20
|
<li class="pw-breadcrumb-item active">
|
|
18
21
|
{{subcategory.name}}
|
|
19
22
|
</li>
|
|
@@ -44,12 +47,14 @@
|
|
|
44
47
|
<colgroup>
|
|
45
48
|
<col span="1">
|
|
46
49
|
<col span="1" width="96px">
|
|
50
|
+
<col span="1" width="96px">
|
|
47
51
|
<col span="1" width="160px">
|
|
48
52
|
</colgroup>
|
|
49
53
|
<thead>
|
|
50
54
|
<tr>
|
|
51
55
|
<th>Title</th>
|
|
52
56
|
<th>Posts</th>
|
|
57
|
+
<th>Views</th>
|
|
53
58
|
<th></th>
|
|
54
59
|
</tr>
|
|
55
60
|
</thead>
|
|
@@ -74,6 +79,7 @@
|
|
|
74
79
|
{{thread.created_at|date:'M j, Y'}}</div>
|
|
75
80
|
</td>
|
|
76
81
|
<td>{{thread.post_count}}</td>
|
|
82
|
+
<td>{{thread.view_count | humanize_int}}</td>
|
|
77
83
|
<td>
|
|
78
84
|
{% if thread.latest_post %}
|
|
79
85
|
<div class="thread__latestPost">
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
<li class="pw-breadcrumb-item">
|
|
22
22
|
<a href="{% url 'punkweb_bb:index' %}">Home</a>
|
|
23
23
|
</li>
|
|
24
|
+
<li class="pw-breadcrumb-item">
|
|
25
|
+
<a href="{{thread.subcategory.category.get_absolute_url}}">{{thread.subcategory.category.name}}</a>
|
|
26
|
+
</li>
|
|
24
27
|
<li class="pw-breadcrumb-item">
|
|
25
28
|
<a href="{{thread.subcategory.get_absolute_url}}">{{thread.subcategory.name}}</a>
|
|
26
29
|
</li>
|
|
@@ -56,7 +59,7 @@
|
|
|
56
59
|
<div class="thread__user__info__row">
|
|
57
60
|
<div class="thread__user__info__label">Joined:</div>
|
|
58
61
|
<div class="thread__user__info__value">
|
|
59
|
-
{{thread.user.
|
|
62
|
+
{{thread.user.date_joined|date:"M d, Y"}}
|
|
60
63
|
</div>
|
|
61
64
|
</div>
|
|
62
65
|
<div class="thread__user__info__row">
|
|
@@ -117,7 +120,7 @@
|
|
|
117
120
|
<div class="thread__user__info__row">
|
|
118
121
|
<div class="thread__user__info__label">Joined:</div>
|
|
119
122
|
<div class="thread__user__info__value">
|
|
120
|
-
{{post.user.
|
|
123
|
+
{{post.user.date_joined|date:"M d, Y"}}
|
|
121
124
|
</div>
|
|
122
125
|
</div>
|
|
123
126
|
<div class="thread__user__info__row">
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
<li class="pw-breadcrumb-item">
|
|
16
16
|
<a href="{% url 'punkweb_bb:index' %}">Home</a>
|
|
17
17
|
</li>
|
|
18
|
+
<li class="pw-breadcrumb-item">
|
|
19
|
+
<a href="{{subcategory.category.get_absolute_url}}">{{subcategory.category.name}}</a>
|
|
20
|
+
</li>
|
|
18
21
|
<li class="pw-breadcrumb-item">
|
|
19
22
|
<a href="{{subcategory.get_absolute_url}}">{{subcategory.name}}</a>
|
|
20
23
|
</li>
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
<li class="pw-breadcrumb-item">
|
|
16
16
|
<a href="{% url 'punkweb_bb:index' %}">Home</a>
|
|
17
17
|
</li>
|
|
18
|
+
<li class="pw-breadcrumb-item">
|
|
19
|
+
<a href="{{thread.subcategory.category.get_absolute_url}}">{{thread.subcategory.category.name}}</a>
|
|
20
|
+
</li>
|
|
18
21
|
<li class="pw-breadcrumb-item">
|
|
19
22
|
<a href="{{thread.subcategory.get_absolute_url}}">{{thread.subcategory.name}}</a>
|
|
20
23
|
</li>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
punkweb_bb/tests.py
CHANGED
|
@@ -2,7 +2,9 @@ import math
|
|
|
2
2
|
|
|
3
3
|
from django.contrib.auth import get_user_model
|
|
4
4
|
from django.core.cache import cache
|
|
5
|
-
from django.
|
|
5
|
+
from django.forms import ValidationError
|
|
6
|
+
from django.test import Client, TestCase
|
|
7
|
+
from django.urls import reverse
|
|
6
8
|
from django.utils import timezone
|
|
7
9
|
|
|
8
10
|
from punkweb_bb.models import (
|
|
@@ -141,7 +143,7 @@ class ThreadTestCase(TestCase):
|
|
|
141
143
|
self.user = User.objects.create_user(username="test", password="test")
|
|
142
144
|
self.category = Category.objects.create(name="test")
|
|
143
145
|
self.subcategory = Subcategory.objects.create(
|
|
144
|
-
name="test", category=self.category
|
|
146
|
+
name="test", category=self.category, slug="test"
|
|
145
147
|
)
|
|
146
148
|
|
|
147
149
|
def test_thread_str(self):
|
|
@@ -175,6 +177,64 @@ class ThreadTestCase(TestCase):
|
|
|
175
177
|
|
|
176
178
|
self.assertEqual(thread.latest_post, post_2)
|
|
177
179
|
|
|
180
|
+
def test_last_post_created_at(self):
|
|
181
|
+
thread = Thread.objects.create(
|
|
182
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
183
|
+
)
|
|
184
|
+
initial_last_post_created_at = thread.last_post_created_at
|
|
185
|
+
Post.objects.create(thread=thread, user=self.user, content="test")
|
|
186
|
+
new_last_post_created_at = thread.last_post_created_at
|
|
187
|
+
|
|
188
|
+
self.assertGreater(new_last_post_created_at, initial_last_post_created_at)
|
|
189
|
+
|
|
190
|
+
def test_is_closed(self):
|
|
191
|
+
thread = Thread.objects.create(
|
|
192
|
+
subcategory=self.subcategory,
|
|
193
|
+
user=self.user,
|
|
194
|
+
title="test",
|
|
195
|
+
content="test",
|
|
196
|
+
is_closed=True,
|
|
197
|
+
)
|
|
198
|
+
self.assertEqual(thread.is_closed, True)
|
|
199
|
+
|
|
200
|
+
post = Post(
|
|
201
|
+
thread=thread,
|
|
202
|
+
user=self.user,
|
|
203
|
+
content="test",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
self.assertRaises(ValidationError, post.save)
|
|
207
|
+
|
|
208
|
+
def test_is_pinned(self):
|
|
209
|
+
thread = Thread.objects.create(
|
|
210
|
+
subcategory=self.subcategory,
|
|
211
|
+
user=self.user,
|
|
212
|
+
title="test1",
|
|
213
|
+
content="test1",
|
|
214
|
+
is_pinned=True,
|
|
215
|
+
)
|
|
216
|
+
self.assertEqual(thread.is_pinned, True)
|
|
217
|
+
|
|
218
|
+
Thread.objects.create(
|
|
219
|
+
subcategory=self.subcategory, user=self.user, title="test2", content="test2"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
threads = Thread.objects.all()
|
|
223
|
+
|
|
224
|
+
self.assertEqual(threads[0], thread)
|
|
225
|
+
|
|
226
|
+
def test_bump(self):
|
|
227
|
+
thread1 = Thread.objects.create(
|
|
228
|
+
subcategory=self.subcategory, user=self.user, title="test1", content="test1"
|
|
229
|
+
)
|
|
230
|
+
Thread.objects.create(
|
|
231
|
+
subcategory=self.subcategory, user=self.user, title="test2", content="test2"
|
|
232
|
+
)
|
|
233
|
+
Post.objects.create(thread=thread1, user=self.user, content="test1")
|
|
234
|
+
threads = Thread.objects.all()
|
|
235
|
+
|
|
236
|
+
self.assertEqual(threads[0], thread1)
|
|
237
|
+
|
|
178
238
|
def test_get_absolute_url(self):
|
|
179
239
|
thread = Thread.objects.create(
|
|
180
240
|
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
@@ -228,3 +288,459 @@ class ShoutTestCase(TestCase):
|
|
|
228
288
|
def test_str(self):
|
|
229
289
|
shout = Shout.objects.create(user=self.user, content="test")
|
|
230
290
|
self.assertEqual(str(shout), f"{shout.user} > {shout.created_at}")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class IndexViewTestCase(TestCase):
|
|
294
|
+
def setUp(self):
|
|
295
|
+
self.client = Client()
|
|
296
|
+
self.url = reverse("punkweb_bb:index")
|
|
297
|
+
self.user = User.objects.create(username="test", password="test")
|
|
298
|
+
self.staff_user = User.objects.create(
|
|
299
|
+
username="staff", password="staff", is_staff=True
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def test_users_online(self):
|
|
303
|
+
response = self.client.get(self.url)
|
|
304
|
+
self.assertEqual(len(response.context["users_online"]), 0)
|
|
305
|
+
|
|
306
|
+
self.client.force_login(self.user)
|
|
307
|
+
response = self.client.get(self.url)
|
|
308
|
+
|
|
309
|
+
self.assertEqual(len(response.context["users_online"]), 1)
|
|
310
|
+
|
|
311
|
+
def test_staff_online(self):
|
|
312
|
+
response = self.client.get(self.url)
|
|
313
|
+
self.assertEqual(len(response.context["staff_online"]), 0)
|
|
314
|
+
|
|
315
|
+
self.client.force_login(self.staff_user)
|
|
316
|
+
response = self.client.get(self.url)
|
|
317
|
+
|
|
318
|
+
self.assertEqual(len(response.context["staff_online"]), 1)
|
|
319
|
+
|
|
320
|
+
def test_newest_user(self):
|
|
321
|
+
response = self.client.get(self.url)
|
|
322
|
+
self.assertEqual(response.context["newest_user"], self.staff_user)
|
|
323
|
+
|
|
324
|
+
def test_users_count(self):
|
|
325
|
+
response = self.client.get(self.url)
|
|
326
|
+
self.assertEqual(response.context["users"].count(), 2)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class LoginViewTestCase(TestCase):
|
|
330
|
+
def setUp(self):
|
|
331
|
+
self.client = Client()
|
|
332
|
+
self.url = reverse("punkweb_bb:login")
|
|
333
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
334
|
+
|
|
335
|
+
def test_redirect_authenticated_user(self):
|
|
336
|
+
self.client.force_login(self.user)
|
|
337
|
+
response = self.client.get(self.url)
|
|
338
|
+
|
|
339
|
+
self.assertRedirects(response, reverse("punkweb_bb:index"))
|
|
340
|
+
|
|
341
|
+
def test_login(self):
|
|
342
|
+
response = self.client.post(
|
|
343
|
+
self.url, {"username": "test", "password": "test"}, follow=True
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
self.assertRedirects(response, reverse("punkweb_bb:index"))
|
|
347
|
+
self.assertTrue(response.context["user"].is_authenticated)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class LogoutViewTestCase(TestCase):
|
|
351
|
+
def setUp(self):
|
|
352
|
+
self.client = Client()
|
|
353
|
+
self.url = reverse("punkweb_bb:logout")
|
|
354
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
355
|
+
|
|
356
|
+
def test_logout(self):
|
|
357
|
+
self.client.force_login(self.user)
|
|
358
|
+
response = self.client.get(self.url, follow=True)
|
|
359
|
+
|
|
360
|
+
self.assertRedirects(response, reverse("punkweb_bb:login"))
|
|
361
|
+
self.assertFalse(response.context["user"].is_authenticated)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class SignupViewTestCase(TestCase):
|
|
365
|
+
def setUp(self):
|
|
366
|
+
self.client = Client()
|
|
367
|
+
self.url = reverse("punkweb_bb:signup")
|
|
368
|
+
self.user = User.objects.create_user(username="test1", password="test")
|
|
369
|
+
|
|
370
|
+
def test_redirect_authenticated_user(self):
|
|
371
|
+
self.client.force_login(self.user)
|
|
372
|
+
response = self.client.get(self.url)
|
|
373
|
+
|
|
374
|
+
self.assertRedirects(response, reverse("punkweb_bb:index"))
|
|
375
|
+
|
|
376
|
+
def test_signup(self):
|
|
377
|
+
response = self.client.post(
|
|
378
|
+
self.url,
|
|
379
|
+
{
|
|
380
|
+
"username": "test2",
|
|
381
|
+
"password1": "needsmorecomplexity",
|
|
382
|
+
"password2": "needsmorecomplexity",
|
|
383
|
+
},
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
self.assertRedirects(response, reverse("punkweb_bb:login"))
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class SettingsViewTestCase(TestCase):
|
|
390
|
+
def setUp(self):
|
|
391
|
+
self.client = Client()
|
|
392
|
+
self.url = reverse("punkweb_bb:settings")
|
|
393
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
394
|
+
|
|
395
|
+
def test_redirect_unauthenticated_user(self):
|
|
396
|
+
response = self.client.get(self.url)
|
|
397
|
+
|
|
398
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
399
|
+
|
|
400
|
+
def test_settings(self):
|
|
401
|
+
self.client.force_login(self.user)
|
|
402
|
+
response = self.client.get(self.url)
|
|
403
|
+
|
|
404
|
+
self.assertEqual(response.status_code, 200)
|
|
405
|
+
|
|
406
|
+
self.client.post(
|
|
407
|
+
self.url,
|
|
408
|
+
{
|
|
409
|
+
"signature": "[b]test[/b]",
|
|
410
|
+
},
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
self.user.profile.refresh_from_db()
|
|
414
|
+
self.assertEqual(self.user.profile._signature_rendered, "<strong>test</strong>")
|
|
415
|
+
|
|
416
|
+
self.assertEqual(response.status_code, 200)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class ThreadCreateViewTestCase(TestCase):
|
|
420
|
+
def setUp(self):
|
|
421
|
+
self.client = Client()
|
|
422
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
423
|
+
self.category = Category.objects.create(name="test")
|
|
424
|
+
self.subcategory = Subcategory.objects.create(
|
|
425
|
+
name="test", category=self.category, slug="test"
|
|
426
|
+
)
|
|
427
|
+
self.staff_subcategory = Subcategory.objects.create(
|
|
428
|
+
name="staff", category=self.category, slug="staff", staff_post_only=True
|
|
429
|
+
)
|
|
430
|
+
self.url = reverse("punkweb_bb:thread_create", args=[self.subcategory.slug])
|
|
431
|
+
self.staff_only_url = reverse(
|
|
432
|
+
"punkweb_bb:thread_create", args=[self.staff_subcategory.slug]
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
def test_redirect_unauthenticated_user(self):
|
|
436
|
+
response = self.client.get(self.url)
|
|
437
|
+
|
|
438
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
439
|
+
|
|
440
|
+
def test_thread_create(self):
|
|
441
|
+
self.client.force_login(self.user)
|
|
442
|
+
response = self.client.get(self.url)
|
|
443
|
+
|
|
444
|
+
self.assertEqual(response.status_code, 200)
|
|
445
|
+
|
|
446
|
+
response = self.client.post(
|
|
447
|
+
self.url,
|
|
448
|
+
{
|
|
449
|
+
"title": "test",
|
|
450
|
+
"content": "test",
|
|
451
|
+
},
|
|
452
|
+
follow=True,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
new_thread = Thread.objects.first()
|
|
456
|
+
|
|
457
|
+
self.assertRedirects(response, new_thread.get_absolute_url())
|
|
458
|
+
self.assertEqual(Thread.objects.count(), 1)
|
|
459
|
+
self.assertEqual(new_thread.user, self.user)
|
|
460
|
+
self.assertEqual(new_thread.subcategory, self.subcategory)
|
|
461
|
+
|
|
462
|
+
def test_thread_create_staff_post_only(self):
|
|
463
|
+
self.client.force_login(self.user)
|
|
464
|
+
response = self.client.get(self.staff_only_url)
|
|
465
|
+
|
|
466
|
+
self.assertRedirects(response, self.staff_subcategory.get_absolute_url())
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class ThreadViewTestCase(TestCase):
|
|
470
|
+
def setUp(self):
|
|
471
|
+
self.client = Client()
|
|
472
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
473
|
+
self.category = Category.objects.create(name="test")
|
|
474
|
+
self.subcategory = Subcategory.objects.create(
|
|
475
|
+
name="test", category=self.category, slug="test"
|
|
476
|
+
)
|
|
477
|
+
self.thread = Thread.objects.create(
|
|
478
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
479
|
+
)
|
|
480
|
+
self.url = reverse("punkweb_bb:thread", args=[self.thread.id])
|
|
481
|
+
|
|
482
|
+
def test_view_count(self):
|
|
483
|
+
response = self.client.get(self.url)
|
|
484
|
+
self.assertEqual(response.context["thread"].view_count, 1)
|
|
485
|
+
|
|
486
|
+
# ensure that the view count does not increase when viewed again from the same session
|
|
487
|
+
|
|
488
|
+
response = self.client.get(self.url)
|
|
489
|
+
self.assertEqual(response.context["thread"].view_count, 1)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class ThreadUpdateViewTestCase(TestCase):
|
|
493
|
+
def setUp(self):
|
|
494
|
+
self.client = Client()
|
|
495
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
496
|
+
self.other_user = User.objects.create_user(username="other", password="other")
|
|
497
|
+
self.category = Category.objects.create(name="test")
|
|
498
|
+
self.subcategory = Subcategory.objects.create(
|
|
499
|
+
name="test", category=self.category, slug="test"
|
|
500
|
+
)
|
|
501
|
+
self.thread = Thread.objects.create(
|
|
502
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
503
|
+
)
|
|
504
|
+
self.url = reverse("punkweb_bb:thread_update", args=[self.thread.id])
|
|
505
|
+
|
|
506
|
+
def test_redirect_unauthenticated_user(self):
|
|
507
|
+
response = self.client.get(self.url)
|
|
508
|
+
|
|
509
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
510
|
+
|
|
511
|
+
def test_is_author(self):
|
|
512
|
+
self.client.force_login(self.other_user)
|
|
513
|
+
response = self.client.get(self.url)
|
|
514
|
+
self.assertEqual(response.status_code, 404)
|
|
515
|
+
|
|
516
|
+
self.client.force_login(self.user)
|
|
517
|
+
response = self.client.get(self.url)
|
|
518
|
+
self.assertEqual(response.status_code, 200)
|
|
519
|
+
|
|
520
|
+
def test_thread_update(self):
|
|
521
|
+
self.client.force_login(self.user)
|
|
522
|
+
response = self.client.get(self.url)
|
|
523
|
+
|
|
524
|
+
self.assertEqual(response.status_code, 200)
|
|
525
|
+
|
|
526
|
+
response = self.client.post(
|
|
527
|
+
self.url,
|
|
528
|
+
{
|
|
529
|
+
"title": "edit",
|
|
530
|
+
"content": "edit",
|
|
531
|
+
},
|
|
532
|
+
follow=True,
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
self.assertRedirects(response, self.thread.get_absolute_url())
|
|
536
|
+
self.thread.refresh_from_db()
|
|
537
|
+
self.assertEqual(self.thread.title, "edit")
|
|
538
|
+
self.assertEqual(self.thread._content_rendered, "edit")
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class ThreadDeleteViewTestCase(TestCase):
|
|
542
|
+
def setUp(self):
|
|
543
|
+
self.client = Client()
|
|
544
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
545
|
+
self.other_user = User.objects.create_user(username="other", password="other")
|
|
546
|
+
self.category = Category.objects.create(name="test")
|
|
547
|
+
self.subcategory = Subcategory.objects.create(
|
|
548
|
+
name="test", category=self.category, slug="test"
|
|
549
|
+
)
|
|
550
|
+
self.thread = Thread.objects.create(
|
|
551
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
552
|
+
)
|
|
553
|
+
self.url = reverse("punkweb_bb:thread_delete", args=[self.thread.id])
|
|
554
|
+
|
|
555
|
+
def test_redirect_unauthenticated_user(self):
|
|
556
|
+
response = self.client.get(self.url)
|
|
557
|
+
|
|
558
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
559
|
+
|
|
560
|
+
def test_is_author(self):
|
|
561
|
+
self.client.force_login(self.other_user)
|
|
562
|
+
response = self.client.get(self.url)
|
|
563
|
+
self.assertEqual(response.status_code, 404)
|
|
564
|
+
|
|
565
|
+
self.client.force_login(self.user)
|
|
566
|
+
response = self.client.get(self.url)
|
|
567
|
+
self.assertEqual(response.status_code, 200)
|
|
568
|
+
|
|
569
|
+
def test_thread_delete(self):
|
|
570
|
+
self.client.force_login(self.user)
|
|
571
|
+
response = self.client.get(self.url)
|
|
572
|
+
|
|
573
|
+
self.assertEqual(response.status_code, 200)
|
|
574
|
+
|
|
575
|
+
response = self.client.delete(self.url, follow=True)
|
|
576
|
+
|
|
577
|
+
self.assertEqual(
|
|
578
|
+
response.headers["HX-Redirect"], self.subcategory.get_absolute_url()
|
|
579
|
+
)
|
|
580
|
+
self.assertEqual(Thread.objects.count(), 0)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class PostCreateViewTestCase(TestCase):
|
|
584
|
+
def setUp(self):
|
|
585
|
+
self.client = Client()
|
|
586
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
587
|
+
self.category = Category.objects.create(name="test")
|
|
588
|
+
self.subcategory = Subcategory.objects.create(
|
|
589
|
+
name="test", category=self.category, slug="test"
|
|
590
|
+
)
|
|
591
|
+
self.thread = Thread.objects.create(
|
|
592
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
593
|
+
)
|
|
594
|
+
self.url = reverse("punkweb_bb:post_create", args=[self.thread.id])
|
|
595
|
+
|
|
596
|
+
def test_redirect_unauthenticated_user(self):
|
|
597
|
+
response = self.client.get(self.url)
|
|
598
|
+
|
|
599
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
600
|
+
|
|
601
|
+
def test_post_create(self):
|
|
602
|
+
self.client.force_login(self.user)
|
|
603
|
+
|
|
604
|
+
response = self.client.post(
|
|
605
|
+
self.url,
|
|
606
|
+
{
|
|
607
|
+
"content": "test",
|
|
608
|
+
},
|
|
609
|
+
follow=True,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
new_post = Post.objects.first()
|
|
613
|
+
|
|
614
|
+
self.assertRedirects(response, new_post.get_absolute_url())
|
|
615
|
+
self.assertEqual(Post.objects.count(), 1)
|
|
616
|
+
self.assertEqual(new_post.user, self.user)
|
|
617
|
+
self.assertEqual(new_post.thread, self.thread)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
class PostUpdateViewTestCase(TestCase):
|
|
621
|
+
def setUp(self):
|
|
622
|
+
self.client = Client()
|
|
623
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
624
|
+
self.other_user = User.objects.create_user(username="other", password="other")
|
|
625
|
+
self.category = Category.objects.create(name="test")
|
|
626
|
+
self.subcategory = Subcategory.objects.create(
|
|
627
|
+
name="test", category=self.category, slug="test"
|
|
628
|
+
)
|
|
629
|
+
self.thread = Thread.objects.create(
|
|
630
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
631
|
+
)
|
|
632
|
+
self.post = Post.objects.create(
|
|
633
|
+
thread=self.thread, user=self.user, content="test"
|
|
634
|
+
)
|
|
635
|
+
self.url = reverse("punkweb_bb:post_update", args=[self.post.id])
|
|
636
|
+
|
|
637
|
+
def test_redirect_unauthenticated_user(self):
|
|
638
|
+
response = self.client.get(self.url)
|
|
639
|
+
|
|
640
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
641
|
+
|
|
642
|
+
def test_is_author(self):
|
|
643
|
+
self.client.force_login(self.other_user)
|
|
644
|
+
response = self.client.get(self.url)
|
|
645
|
+
self.assertEqual(response.status_code, 404)
|
|
646
|
+
|
|
647
|
+
self.client.force_login(self.user)
|
|
648
|
+
response = self.client.get(self.url)
|
|
649
|
+
self.assertEqual(response.status_code, 200)
|
|
650
|
+
|
|
651
|
+
def test_post_update(self):
|
|
652
|
+
self.client.force_login(self.user)
|
|
653
|
+
response = self.client.get(self.url)
|
|
654
|
+
|
|
655
|
+
self.assertEqual(response.status_code, 200)
|
|
656
|
+
|
|
657
|
+
response = self.client.post(
|
|
658
|
+
self.url,
|
|
659
|
+
{
|
|
660
|
+
"content": "edit",
|
|
661
|
+
},
|
|
662
|
+
follow=True,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
self.assertRedirects(response, self.post.get_absolute_url())
|
|
666
|
+
self.post.refresh_from_db()
|
|
667
|
+
self.assertEqual(self.post._content_rendered, "edit")
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
class PostDeleteViewTestCase(TestCase):
|
|
671
|
+
def setUp(self):
|
|
672
|
+
self.client = Client()
|
|
673
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
674
|
+
self.other_user = User.objects.create_user(username="other", password="other")
|
|
675
|
+
self.category = Category.objects.create(name="test")
|
|
676
|
+
self.subcategory = Subcategory.objects.create(
|
|
677
|
+
name="test", category=self.category, slug="test"
|
|
678
|
+
)
|
|
679
|
+
self.thread = Thread.objects.create(
|
|
680
|
+
subcategory=self.subcategory, user=self.user, title="test", content="test"
|
|
681
|
+
)
|
|
682
|
+
self.post = Post.objects.create(
|
|
683
|
+
thread=self.thread, user=self.user, content="test"
|
|
684
|
+
)
|
|
685
|
+
self.url = reverse("punkweb_bb:post_delete", args=[self.post.id])
|
|
686
|
+
|
|
687
|
+
def test_redirect_unauthenticated_user(self):
|
|
688
|
+
response = self.client.get(self.url)
|
|
689
|
+
|
|
690
|
+
self.assertRedirects(response, f"{reverse('punkweb_bb:login')}?next={self.url}")
|
|
691
|
+
|
|
692
|
+
def test_is_author(self):
|
|
693
|
+
self.client.force_login(self.other_user)
|
|
694
|
+
response = self.client.get(self.url)
|
|
695
|
+
self.assertEqual(response.status_code, 404)
|
|
696
|
+
|
|
697
|
+
self.client.force_login(self.user)
|
|
698
|
+
response = self.client.get(self.url)
|
|
699
|
+
self.assertEqual(response.status_code, 200)
|
|
700
|
+
|
|
701
|
+
def test_post_delete(self):
|
|
702
|
+
self.client.force_login(self.user)
|
|
703
|
+
response = self.client.get(self.url)
|
|
704
|
+
|
|
705
|
+
self.assertEqual(response.status_code, 200)
|
|
706
|
+
|
|
707
|
+
response = self.client.delete(self.url, follow=True)
|
|
708
|
+
|
|
709
|
+
self.assertEqual(
|
|
710
|
+
response.headers["HX-Redirect"], self.thread.get_absolute_url()
|
|
711
|
+
)
|
|
712
|
+
self.assertEqual(Post.objects.count(), 0)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
class ShoutCreateViewTestCase(TestCase):
|
|
716
|
+
def setUp(self):
|
|
717
|
+
self.client = Client()
|
|
718
|
+
self.user = User.objects.create_user(username="test", password="test")
|
|
719
|
+
self.url = reverse("punkweb_bb:shout_create")
|
|
720
|
+
|
|
721
|
+
def test_unauthenticated(self):
|
|
722
|
+
response = self.client.get(self.url)
|
|
723
|
+
|
|
724
|
+
self.client.post(
|
|
725
|
+
self.url,
|
|
726
|
+
{
|
|
727
|
+
"content": "test",
|
|
728
|
+
},
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
self.assertEqual(Shout.objects.count(), 0)
|
|
732
|
+
self.assertEqual(response.status_code, 200)
|
|
733
|
+
|
|
734
|
+
def test_shout_create(self):
|
|
735
|
+
self.client.force_login(self.user)
|
|
736
|
+
|
|
737
|
+
response = self.client.post(
|
|
738
|
+
self.url,
|
|
739
|
+
{
|
|
740
|
+
"content": "test",
|
|
741
|
+
},
|
|
742
|
+
follow=True,
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
self.assertEqual(Shout.objects.count(), 1)
|
|
746
|
+
self.assertEqual(response.status_code, 200)
|
punkweb_bb/views.py
CHANGED
|
@@ -108,7 +108,9 @@ def profile_view(request, user_id):
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
def members_view(request):
|
|
111
|
-
users = paginate_qs(
|
|
111
|
+
users = paginate_qs(
|
|
112
|
+
request, User.objects.select_related("profile").order_by("username")
|
|
113
|
+
)
|
|
112
114
|
|
|
113
115
|
context = {
|
|
114
116
|
"users": users,
|
|
@@ -185,6 +187,13 @@ def thread_view(request, thread_id):
|
|
|
185
187
|
|
|
186
188
|
post_form = PostModelForm()
|
|
187
189
|
|
|
190
|
+
# Increment view count if this session hasn't viewed the thread before
|
|
191
|
+
viewed_threads = request.session.get("viewed_threads", [])
|
|
192
|
+
if thread_id not in viewed_threads:
|
|
193
|
+
thread.view_count += 1
|
|
194
|
+
thread.save()
|
|
195
|
+
request.session["viewed_threads"] = viewed_threads + [thread_id]
|
|
196
|
+
|
|
188
197
|
context = {
|
|
189
198
|
"thread": thread,
|
|
190
199
|
"posts": posts,
|
|
@@ -235,7 +244,7 @@ def post_create_view(request, thread_id):
|
|
|
235
244
|
thread = get_object_or_404(Thread, pk=thread_id)
|
|
236
245
|
|
|
237
246
|
if thread.is_closed:
|
|
238
|
-
return HttpResponseForbidden("
|
|
247
|
+
return HttpResponseForbidden("Cannot add posts to a closed thread.")
|
|
239
248
|
|
|
240
249
|
form = PostModelForm(request.POST)
|
|
241
250
|
|
|
@@ -336,7 +345,7 @@ def bbcode_view(request):
|
|
|
336
345
|
("Url", "[url=https://google.com]Link Text[/url]"),
|
|
337
346
|
(
|
|
338
347
|
"Image",
|
|
339
|
-
"[img]https://
|
|
348
|
+
"[img]https://placehold.co/400[/img]",
|
|
340
349
|
),
|
|
341
350
|
# Custom
|
|
342
351
|
("Horizontal Rule", "[hr]"),
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: punkweb-bb
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Django application that provides a simple and modern forum board software for your Django website.
|
|
5
|
+
Home-page: https://github.com/Punkweb/PunkwebBB
|
|
6
|
+
Author: Punkweb
|
|
7
|
+
Author-email: punkwebnet@gmail.com
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Classifier: Environment :: Web Environment
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: django >=4.0
|
|
19
|
+
Requires-Dist: django-precise-bbcode
|
|
20
|
+
Requires-Dist: pillow
|
|
21
|
+
|
|
22
|
+
# PunkwebBB
|
|
23
|
+
|
|
24
|
+
PunkwebBB is a Django application that provides a simple and modern forum board software for your Django website.
|
|
25
|
+
|
|
26
|
+
This is the successor to [punkweb-boards](https://github.com/Punkweb/punkweb-boards).
|
|
27
|
+
|
|
28
|
+
Check out [punkweb.net](https://punkweb.net/board/) for a live demo and more information!
|
|
29
|
+
|
|
30
|
+
## Built with
|
|
31
|
+
|
|
32
|
+
- [Django](https://www.djangoproject.com/)
|
|
33
|
+
- [django-precise-bbcode](https://github.com/ellmetha/django-precise-bbcode)
|
|
34
|
+
- [HTMX](https://htmx.org/)
|
|
35
|
+
- [jQuery](https://jquery.com/)
|
|
36
|
+
- [SCEditor](https://www.sceditor.com/)
|
|
37
|
+
- [PrismJS](https://prismjs.com/)
|
|
38
|
+
|
|
39
|
+
## Requirements
|
|
40
|
+
|
|
41
|
+
- Python 3.11+
|
|
42
|
+
- Django 4.2+
|
|
43
|
+
- django-precise-bbcode 1.2+
|
|
44
|
+
- Pillow
|
|
45
|
+
|
|
46
|
+
It may work with older versions of Python and Django, but it has not been tested.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install punkweb-bb
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Add `precise_bbcode` and `punkweb_bb` to your `INSTALLED_APPS` in your Django settings module:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
INSTALLED_APPS = [
|
|
58
|
+
...
|
|
59
|
+
"precise_bbcode",
|
|
60
|
+
"punkweb_bb",
|
|
61
|
+
]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
_Note_: `precise_bbcode` is required. It must be installed before `punkweb_bb`.
|
|
65
|
+
|
|
66
|
+
Add the following middleware to your `MIDDLEWARE` setting:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
MIDDLEWARE = [
|
|
70
|
+
...
|
|
71
|
+
"punkweb_bb.middleware.ProfileOnlineCacheMiddleware",
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Add the following context processor to your `TEMPLATES` setting:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
TEMPLATES = [
|
|
79
|
+
{
|
|
80
|
+
...
|
|
81
|
+
"OPTIONS": {
|
|
82
|
+
"context_processors": [
|
|
83
|
+
...
|
|
84
|
+
"punkweb_bb.context_processors.punkweb_bb",
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Add the following URL pattern to your `urls.py`:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from django.urls import path, include
|
|
95
|
+
|
|
96
|
+
urlpatterns = [
|
|
97
|
+
...
|
|
98
|
+
path("forum/", include("punkweb_bb.urls")), # or any other path you want
|
|
99
|
+
]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
And finally, install the models:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python manage.py migrate
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
These are the default settings for PunkwebBB, which can be overridden in your Django settings module:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
PUNKWEB_BB = {
|
|
114
|
+
"SITE_NAME": "PUNKWEB",
|
|
115
|
+
"SITE_TITLE": "PunkwebBB",
|
|
116
|
+
"FAVICON": "punkweb_bb/favicon.ico",
|
|
117
|
+
"SHOUTBOX_ENABLED": True,
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Testing
|
|
122
|
+
|
|
123
|
+
Report:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
coverage run && coverage report
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
HTML:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
coverage run && coverage html
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
Found 57 test(s).
|
|
137
|
+
Creating test database for alias 'default'...
|
|
138
|
+
System check identified no issues (0 silenced).
|
|
139
|
+
.........................................................
|
|
140
|
+
----------------------------------------------------------------------
|
|
141
|
+
Ran 57 tests in 8.824s
|
|
142
|
+
|
|
143
|
+
OK
|
|
144
|
+
Destroying test database for alias 'default'...
|
|
145
|
+
Name Stmts Miss Cover
|
|
146
|
+
----------------------------------------------------------------
|
|
147
|
+
punkweb_bb/__init__.py 0 0 100%
|
|
148
|
+
punkweb_bb/admin.py 41 1 98%
|
|
149
|
+
punkweb_bb/admin_forms.py 28 0 100%
|
|
150
|
+
punkweb_bb/apps.py 6 0 100%
|
|
151
|
+
punkweb_bb/bbcode_tags.py 115 3 97%
|
|
152
|
+
punkweb_bb/context_processors.py 3 0 100%
|
|
153
|
+
punkweb_bb/forms.py 35 0 100%
|
|
154
|
+
punkweb_bb/middleware.py 10 0 100%
|
|
155
|
+
punkweb_bb/mixins.py 11 0 100%
|
|
156
|
+
punkweb_bb/models.py 124 0 100%
|
|
157
|
+
punkweb_bb/pagination.py 11 4 64%
|
|
158
|
+
punkweb_bb/parsers.py 50 2 96%
|
|
159
|
+
punkweb_bb/response.py 3 0 100%
|
|
160
|
+
punkweb_bb/settings.py 6 0 100%
|
|
161
|
+
punkweb_bb/signals.py 9 0 100%
|
|
162
|
+
punkweb_bb/templatetags/__init__.py 0 0 100%
|
|
163
|
+
punkweb_bb/templatetags/humanize_int.py 9 5 44%
|
|
164
|
+
punkweb_bb/templatetags/shoutbox_bbcode.py 9 0 100%
|
|
165
|
+
punkweb_bb/tests.py 410 0 100%
|
|
166
|
+
punkweb_bb/urls.py 4 0 100%
|
|
167
|
+
punkweb_bb/views.py 174 17 90%
|
|
168
|
+
punkweb_bb/widgets.py 8 0 100%
|
|
169
|
+
----------------------------------------------------------------
|
|
170
|
+
TOTAL 1066 32 97%
|
|
171
|
+
```
|
|
@@ -7,15 +7,15 @@ punkweb_bb/context_processors.py,sha256=BEOGvWVukvxJUxWZBIc32ioB-mGqImSEBuhhZjqI
|
|
|
7
7
|
punkweb_bb/forms.py,sha256=O1lzmgumdnSEjBKi10c7ODftIqRmvtEIdNj1IODCikk,1977
|
|
8
8
|
punkweb_bb/middleware.py,sha256=lF1w7XM07s1kcrOWodzErHop9zpIE5SEE52YLMAbDKg,456
|
|
9
9
|
punkweb_bb/mixins.py,sha256=XfiThPL7rB71IfukS1ikvYQhfg8RwgSVgsm10Ul1ezM,395
|
|
10
|
-
punkweb_bb/models.py,sha256=
|
|
10
|
+
punkweb_bb/models.py,sha256=Y5w80EQ85Mut71xiDVMALqLXdydlTxOiZEljmnH5g-E,5519
|
|
11
11
|
punkweb_bb/pagination.py,sha256=OgoZuJsq9MKMvBKYylJVPaNtM9ni3K8OAvOdi-eGr3M,409
|
|
12
12
|
punkweb_bb/parsers.py,sha256=VjWSPqpVfypHLHP0NrfLqXB-1b0W6oFuGIzoAiPi71I,2732
|
|
13
13
|
punkweb_bb/response.py,sha256=dETGVC9Xrsq02pQzmIIWbSUt472lJ4fgLwBKrXnP3t4,130
|
|
14
|
-
punkweb_bb/settings.py,sha256=
|
|
14
|
+
punkweb_bb/settings.py,sha256=rYKMQxbC6rfzOQgcjAoBuMEgKHOITvOX1PrKTweEYKk,312
|
|
15
15
|
punkweb_bb/signals.py,sha256=bVdfg942Mwq-fYDZ1Z52Q0V2BCk1lgzGz8JVZFPnzJ8,365
|
|
16
|
-
punkweb_bb/tests.py,sha256=
|
|
16
|
+
punkweb_bb/tests.py,sha256=BXkvqtr1JbE2WQYkgjse3wp9ky3cwOzQcVHoqNhHJmw,25989
|
|
17
17
|
punkweb_bb/urls.py,sha256=_mRAtaZoKzPZ_uuOCFy6_lOwFCavv26p5kMKD9MH4c4,1538
|
|
18
|
-
punkweb_bb/views.py,sha256=
|
|
18
|
+
punkweb_bb/views.py,sha256=HDJCM8PgDkQu96UbZLFyjSD2K9WIjLhWEO-QBM3APmk,10723
|
|
19
19
|
punkweb_bb/widgets.py,sha256=eF6CB5nnh_6XJadpDzDKgd9incd0VIR63Rnzdr8T2n0,840
|
|
20
20
|
punkweb_bb/__pycache__/__init__.cpython-311.pyc,sha256=3PyxCxoznfadaGt0a7re4j0Ky9z9hblufpcwPB85kK8,161
|
|
21
21
|
punkweb_bb/__pycache__/admin.cpython-311.pyc,sha256=rAPYqRy7hAVZn3qST3ypYdulvH9IBhzWEgvbbAMIMIY,3679
|
|
@@ -26,18 +26,21 @@ punkweb_bb/__pycache__/context_processors.cpython-311.pyc,sha256=P7rEsodXonYexbS
|
|
|
26
26
|
punkweb_bb/__pycache__/forms.cpython-311.pyc,sha256=JRbtAaXyXgG9eDdBjKBkSn8qeZtdxRc7L2_ceE-MpW8,4680
|
|
27
27
|
punkweb_bb/__pycache__/middleware.cpython-311.pyc,sha256=gZN0oVrPh9nIUUni_DvPrhMOYPR98SXrYxgHIsUXX-0,1249
|
|
28
28
|
punkweb_bb/__pycache__/mixins.cpython-311.pyc,sha256=eP1NjqDNYMYXrC45DNkrTqVAUv1vsGBrqPy5U5CB_aw,1478
|
|
29
|
-
punkweb_bb/__pycache__/models.cpython-311.pyc,sha256=
|
|
29
|
+
punkweb_bb/__pycache__/models.cpython-311.pyc,sha256=9W03p0j5_dYnQ4YffZUrN_A5-Bvkbpvfmpr5rOkzyAQ,12308
|
|
30
30
|
punkweb_bb/__pycache__/pagination.cpython-311.pyc,sha256=r54xmtiRp5dm1n2Xa7oElMFFaYFY0RzmMuF3Er4UqEA,990
|
|
31
31
|
punkweb_bb/__pycache__/parsers.cpython-311.pyc,sha256=pp8JZt9V3HhquQMGrhglwLc53GxgNFWjjSnK3Bpxf8o,5335
|
|
32
32
|
punkweb_bb/__pycache__/response.cpython-311.pyc,sha256=CEPckYWZkOrdU2Vow-ni0ZrWEUBww0uJzyr_wv---mM,448
|
|
33
|
-
punkweb_bb/__pycache__/settings.cpython-311.pyc,sha256=
|
|
33
|
+
punkweb_bb/__pycache__/settings.cpython-311.pyc,sha256=ZjE994_MuSW_9Dn_ufsUH-vrld-BvqEwoCF7YL9tbrA,665
|
|
34
34
|
punkweb_bb/__pycache__/signals.cpython-311.pyc,sha256=AHChn7hDdrOmCAwKIKKuFOY-4hyBP9uwTkyb5bnFV-Q,864
|
|
35
|
+
punkweb_bb/__pycache__/tests.cpython-311.pyc,sha256=3CM8FL8P1YXVcgShhXqXjg3Yo3T1amKXlVtHRUZ2d7o,50877
|
|
35
36
|
punkweb_bb/__pycache__/urls.cpython-311.pyc,sha256=80TvNUws6ip1FCts6bubO3DGQJ2yOlB-WReK-qaTnGY,2304
|
|
36
|
-
punkweb_bb/__pycache__/views.cpython-311.pyc,sha256=
|
|
37
|
+
punkweb_bb/__pycache__/views.cpython-311.pyc,sha256=bjqhbTnN0-pvIygCzh0NXY6lNJUjMpz8B51EIlpay94,15592
|
|
37
38
|
punkweb_bb/__pycache__/widgets.cpython-311.pyc,sha256=-RcQ3JapLHw8Bbi4FP05kQJJIa7bnliKPaFpkDCOWvQ,1552
|
|
38
39
|
punkweb_bb/migrations/0001_initial.py,sha256=3RGsylygBcWx1kIPhSzOb9v_2yvowsxKfxuSinKKuS0,8950
|
|
40
|
+
punkweb_bb/migrations/0002_thread_view_count.py,sha256=JJZT53Mp8Ofht3oIi67s-0wzCdpYyu8wOeCi_B8q8Yo,388
|
|
39
41
|
punkweb_bb/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
42
|
punkweb_bb/migrations/__pycache__/0001_initial.cpython-311.pyc,sha256=koi_VflndmTKzSXWstWBXuVEZgEeueId5NE4kOAlA4Q,6456
|
|
43
|
+
punkweb_bb/migrations/__pycache__/0002_thread_view_count.cpython-311.pyc,sha256=9HRllkD8LHPXadyurYvp2UcmRFEywalUVQjiOGWizgc,815
|
|
41
44
|
punkweb_bb/migrations/__pycache__/__init__.cpython-311.pyc,sha256=sTbC1AXnh0V4BJwjcjs1ckdeYjG01I348hZwLE2HI4Y,172
|
|
42
45
|
punkweb_bb/static/punkweb_bb/favicon.ico,sha256=lEfX--R9wEGPkkXgLYCsGmHuAGajigiqBXAoonxq8ZM,318
|
|
43
46
|
punkweb_bb/static/punkweb_bb/css/defaults.css,sha256=EsYORpHIQ8gotAdiGvBBU38i6F0mICj-OKr-JF6yYVg,1455
|
|
@@ -45,7 +48,7 @@ punkweb_bb/static/punkweb_bb/css/index.css,sha256=NqpF7zNMzSrDeHw5gfbwcmeBET4TcS
|
|
|
45
48
|
punkweb_bb/static/punkweb_bb/css/login.css,sha256=pt5ul4ycZsVB-No3c5gsQa1zVS1iAZgteN1CcllS26k,234
|
|
46
49
|
punkweb_bb/static/punkweb_bb/css/members.css,sha256=1Fz0uVDbs3RnuXNjOtnGnK2jok3LEQBoPhjRYp7gNwE,395
|
|
47
50
|
punkweb_bb/static/punkweb_bb/css/post-form.css,sha256=rEiYplAobLjjUYAcnjNqIjyIVhe9O5hAlPJ3STW-K14,321
|
|
48
|
-
punkweb_bb/static/punkweb_bb/css/profile.css,sha256=
|
|
51
|
+
punkweb_bb/static/punkweb_bb/css/profile.css,sha256=yfNJT_D-05deqiBrdIgPeCSO_DFSL8-fGEEHnGrCIYM,916
|
|
49
52
|
punkweb_bb/static/punkweb_bb/css/punkweb-modal.css,sha256=ctwo5bgbAW-k0uqrufDbqeQfAh87-BZ-5gPm8aJHeT4,1296
|
|
50
53
|
punkweb_bb/static/punkweb_bb/css/punkweb.css,sha256=UZT592v_krB8DPFf6YN1zlKmmFiCAXdS_KqO98GrKNM,11284
|
|
51
54
|
punkweb_bb/static/punkweb_bb/css/settings.css,sha256=ulQQFTu8slk2rYOmhvci5v-AVnguUuDhKQDhyQOsQNU,308
|
|
@@ -185,7 +188,7 @@ punkweb_bb/static/punkweb_bb/vendor/sceditor-3.2.0/minified/themes/office-toolba
|
|
|
185
188
|
punkweb_bb/static/punkweb_bb/vendor/sceditor-3.2.0/minified/themes/office.min.css,sha256=EZeNIT-LMxAnrW_7M6BXuH0B8m3MoIS68tDyTxmCoP0,13148
|
|
186
189
|
punkweb_bb/static/punkweb_bb/vendor/sceditor-3.2.0/minified/themes/square.min.css,sha256=vrNHEnpQJr3o8AlJ2aEhn4fsRqR4TOopE3N3-4oE2ho,10710
|
|
187
190
|
punkweb_bb/static/punkweb_bb/vendor/sceditor-3.2.0/minified/themes/content/default.min.css,sha256=2jMxGiqcrAhfOtNMdqmTUtfgM6oUz5F0VJ0sUzam9CY,1016
|
|
188
|
-
punkweb_bb/templates/punkweb_bb/base.html,sha256=
|
|
191
|
+
punkweb_bb/templates/punkweb_bb/base.html,sha256=Gu2ZVeP0p7GnsfXFxByXRd9LT4DNXx0O1czSGjwYg0w,4037
|
|
189
192
|
punkweb_bb/templates/punkweb_bb/base_modal.html,sha256=OCbtsMWeNCO0Tl1PmHCcGkwoi1OZjeIK_VhNTzMor7M,460
|
|
190
193
|
punkweb_bb/templates/punkweb_bb/bbcode.html,sha256=1EGBejsOMZOPi6P39oR6E35VdqnfR6wYWeKDl4Xr_js,396
|
|
191
194
|
punkweb_bb/templates/punkweb_bb/index.html,sha256=2y8gS6UzoJkVXvmw5PEuuxyYIarlNjGzTmmNMuNWNJ8,8127
|
|
@@ -194,21 +197,24 @@ punkweb_bb/templates/punkweb_bb/members.html,sha256=ceRCRX0AN7V8d7paz9495y8aQByM
|
|
|
194
197
|
punkweb_bb/templates/punkweb_bb/profile.html,sha256=MW1JWLs_lLpg62DDcvcZJRwrHquYA5nCwQqL1v9ZGCo,2948
|
|
195
198
|
punkweb_bb/templates/punkweb_bb/settings.html,sha256=CmmGdNddZj6Atpofcny0bwEHwTPRPC8vXXThCgsNH90,1994
|
|
196
199
|
punkweb_bb/templates/punkweb_bb/signup.html,sha256=HP5owUfV2uEuvOccPoBa21XbjBHo0ulV6tjfgdToNLU,1167
|
|
197
|
-
punkweb_bb/templates/punkweb_bb/subcategory.html,sha256=
|
|
198
|
-
punkweb_bb/templates/punkweb_bb/thread.html,sha256=
|
|
199
|
-
punkweb_bb/templates/punkweb_bb/thread_create.html,sha256=
|
|
200
|
-
punkweb_bb/templates/punkweb_bb/thread_update.html,sha256=
|
|
200
|
+
punkweb_bb/templates/punkweb_bb/subcategory.html,sha256=rLJtBKz2Zd07Lyan6fH2fn7i8bXyfVj9hpnBh6m3PS4,4950
|
|
201
|
+
punkweb_bb/templates/punkweb_bb/thread.html,sha256=igyEa1Qz-joktKmUbgHtIY0EnxlWUCBHDdEXNTuKJDk,7595
|
|
202
|
+
punkweb_bb/templates/punkweb_bb/thread_create.html,sha256=rLIO-ghdzHCiKJ6LCj2l4cxe9QbsMUG13ZxogYF9PUE,1537
|
|
203
|
+
punkweb_bb/templates/punkweb_bb/thread_update.html,sha256=SLL_5tceZ8ZiPbWCO9eOe_aeMgV5lQ-p6eun1_XvKwE,1730
|
|
201
204
|
punkweb_bb/templates/punkweb_bb/partials/post_delete.html,sha256=tQtQGxTF0tZA5eXqfIuO6NctsS83eEhR4ImmBLbkAXM,417
|
|
202
205
|
punkweb_bb/templates/punkweb_bb/partials/post_update.html,sha256=mFidDqgTuv4LffuSa8pShXxWmiramLLMhHjsaJs1Y9k,565
|
|
203
206
|
punkweb_bb/templates/punkweb_bb/partials/thread_delete.html,sha256=m91u_r8qTvMqR2s4VxxFHQvZt-WGgHnHzJL_1KZkX9M,425
|
|
204
207
|
punkweb_bb/templates/punkweb_bb/shoutbox/shout_list.html,sha256=uAls--UuLgS6NPW2qEsNbutR_aoJ_AuhZ_Fkt-c8ipU,312
|
|
205
208
|
punkweb_bb/templates/punkweb_bb/shoutbox/shoutbox.html,sha256=J_Lp6KKcqSJr-IayyLN-p0JgMfuwbFP77g-UtcM53WI,672
|
|
206
209
|
punkweb_bb/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
210
|
+
punkweb_bb/templatetags/humanize_int.py,sha256=C4KmDG0Jv6o8rwT1RXLdWoGLddJxMxgOoRV9I2598AM,248
|
|
207
211
|
punkweb_bb/templatetags/shoutbox_bbcode.py,sha256=OH-FsRTyPWZldaFypSVzPLlTrSm4XEOqQW9hBI0ROBk,310
|
|
208
|
-
punkweb_bb/templatetags/__pycache__/__init__.cpython-311.pyc,sha256=
|
|
212
|
+
punkweb_bb/templatetags/__pycache__/__init__.cpython-311.pyc,sha256=VEPKwaIhqpKpSSJiotDYngAUdidDzR9PpPiHtKEl1jA,174
|
|
213
|
+
punkweb_bb/templatetags/__pycache__/humanize_count.cpython-311.pyc,sha256=UKD6_5RX8YpYpg-LPrgGxBkW56THsbYY51cKTYdKwRY,621
|
|
214
|
+
punkweb_bb/templatetags/__pycache__/humanize_int.cpython-311.pyc,sha256=csY5ek-bevYVeM91hYFKozuKWXCTXb7M-7Bokwdxhrk,619
|
|
209
215
|
punkweb_bb/templatetags/__pycache__/shoutbox_bbcode.cpython-311.pyc,sha256=Jhg9qW-nQe6IDr45rE0ZgeDYF4E61S7kYAYpbMo5ZQ8,833
|
|
210
|
-
punkweb_bb-0.1.
|
|
211
|
-
punkweb_bb-0.1.
|
|
212
|
-
punkweb_bb-0.1.
|
|
213
|
-
punkweb_bb-0.1.
|
|
214
|
-
punkweb_bb-0.1.
|
|
216
|
+
punkweb_bb-0.1.3.dist-info/LICENSE,sha256=YYysF07B-kyXSO7IWFB9f49ZXa6LIFUTVsR1Ogmhp8s,1507
|
|
217
|
+
punkweb_bb-0.1.3.dist-info/METADATA,sha256=thU9kjReWNpjs73xcTkXgnO0LKX_-g_wELf87WiE-0Q,4930
|
|
218
|
+
punkweb_bb-0.1.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
219
|
+
punkweb_bb-0.1.3.dist-info/top_level.txt,sha256=sWuGdGnk0ejOXiFDzlBqrNs2VbPEx0_i8UwWXn4SuHU,11
|
|
220
|
+
punkweb_bb-0.1.3.dist-info/RECORD,,
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: punkweb-bb
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: Django application that provides a simple and modern forum board software for your Django website.
|
|
5
|
-
Home-page: https://github.com/Punkweb/PunkwebBB
|
|
6
|
-
Author: Punkweb
|
|
7
|
-
Author-email: punkwebnet@gmail.com
|
|
8
|
-
License: BSD-3-Clause
|
|
9
|
-
Classifier: Environment :: Web Environment
|
|
10
|
-
Classifier: Framework :: Django
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: django >=4.0
|
|
19
|
-
Requires-Dist: django-precise-bbcode
|
|
20
|
-
Requires-Dist: pillow
|
|
21
|
-
|
|
22
|
-
# PunkwebBB
|
|
23
|
-
|
|
24
|
-
PunkwebBB is a Django application that provides a simple and modern forum board software for your Django website.
|
|
25
|
-
|
|
26
|
-
This is the successor to [punkweb-boards](https://github.com/Punkweb/punkweb-boards)
|
|
27
|
-
|
|
28
|
-
## Built with
|
|
29
|
-
|
|
30
|
-
- [Django](https://www.djangoproject.com/)
|
|
31
|
-
- [django-precise-bbcode](https://github.com/ellmetha/django-precise-bbcode)
|
|
32
|
-
- [HTMX](https://htmx.org/)
|
|
33
|
-
- [jQuery](https://jquery.com/)
|
|
34
|
-
- [SCEditor](https://www.sceditor.com/)
|
|
35
|
-
- [PrismJS](https://prismjs.com/)
|
|
36
|
-
|
|
37
|
-
## Requirements
|
|
38
|
-
|
|
39
|
-
- Python 3.11+
|
|
40
|
-
- Django 4.2+
|
|
41
|
-
- django-precise-bbcode 1.2+
|
|
42
|
-
- Pillow
|
|
43
|
-
|
|
44
|
-
It may work with older versions of Python and Django, but it has not been tested.
|
|
45
|
-
|
|
46
|
-
## Testing
|
|
47
|
-
|
|
48
|
-
Report:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
coverage run && coverage report
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
HTML:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
coverage run && coverage html
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Preview
|
|
61
|
-
|
|
62
|
-
#### Models
|
|
63
|
-
|
|
64
|
-

|
|
65
|
-
|
|
66
|
-
#### Index View
|
|
67
|
-
|
|
68
|
-

|
|
69
|
-
|
|
70
|
-
#### Thread View
|
|
71
|
-
|
|
72
|
-

|
|
File without changes
|
|
File without changes
|
|
File without changes
|