punkweb-bb 0.1.3__py3-none-any.whl → 0.2.0__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.
Files changed (49) hide show
  1. punkweb_bb/__pycache__/forms.cpython-311.pyc +0 -0
  2. punkweb_bb/__pycache__/guests.cpython-311.pyc +0 -0
  3. punkweb_bb/__pycache__/middleware.cpython-311.pyc +0 -0
  4. punkweb_bb/__pycache__/models.cpython-311.pyc +0 -0
  5. punkweb_bb/__pycache__/settings.cpython-311.pyc +0 -0
  6. punkweb_bb/__pycache__/tests.cpython-311.pyc +0 -0
  7. punkweb_bb/__pycache__/urls.cpython-311.pyc +0 -0
  8. punkweb_bb/__pycache__/utils.cpython-311.pyc +0 -0
  9. punkweb_bb/__pycache__/views.cpython-311.pyc +0 -0
  10. punkweb_bb/forms.py +40 -7
  11. punkweb_bb/guests.py +20 -0
  12. punkweb_bb/middleware.py +7 -0
  13. punkweb_bb/models.py +21 -0
  14. punkweb_bb/settings.py +3 -0
  15. punkweb_bb/static/punkweb_bb/css/category-form.css +24 -0
  16. punkweb_bb/static/punkweb_bb/css/index.css +6 -0
  17. punkweb_bb/static/punkweb_bb/css/punkweb.css +84 -5
  18. punkweb_bb/static/punkweb_bb/css/shoutbox.css +13 -2
  19. punkweb_bb/static/punkweb_bb/css/subcategory-form.css +24 -0
  20. punkweb_bb/static/punkweb_bb/css/subcategory.css +12 -0
  21. punkweb_bb/static/punkweb_bb/css/thread.css +19 -0
  22. punkweb_bb/static/punkweb_bb/css/variables.css +1 -0
  23. punkweb_bb/templates/punkweb_bb/category_create.html +50 -0
  24. punkweb_bb/templates/punkweb_bb/category_update.html +53 -0
  25. punkweb_bb/templates/punkweb_bb/index.html +46 -7
  26. punkweb_bb/templates/punkweb_bb/partials/category_delete.html +11 -0
  27. punkweb_bb/templates/punkweb_bb/partials/shout_delete.html +11 -0
  28. punkweb_bb/templates/punkweb_bb/partials/subcategory_delete.html +11 -0
  29. punkweb_bb/templates/punkweb_bb/shoutbox/shout_list.html +14 -4
  30. punkweb_bb/templates/punkweb_bb/subcategory.html +21 -6
  31. punkweb_bb/templates/punkweb_bb/subcategory_create.html +53 -0
  32. punkweb_bb/templates/punkweb_bb/subcategory_update.html +56 -0
  33. punkweb_bb/templates/punkweb_bb/thread.html +26 -6
  34. punkweb_bb/templates/punkweb_bb/thread_create.html +1 -0
  35. punkweb_bb/templatetags/__pycache__/can_delete.cpython-311.pyc +0 -0
  36. punkweb_bb/templatetags/__pycache__/can_edit.cpython-311.pyc +0 -0
  37. punkweb_bb/templatetags/__pycache__/can_post.cpython-311.pyc +0 -0
  38. punkweb_bb/templatetags/can_delete.py +8 -0
  39. punkweb_bb/templatetags/can_edit.py +8 -0
  40. punkweb_bb/templatetags/can_post.py +8 -0
  41. punkweb_bb/tests.py +19 -8
  42. punkweb_bb/urls.py +27 -0
  43. punkweb_bb/utils.py +17 -0
  44. punkweb_bb/views.py +232 -38
  45. {punkweb_bb-0.1.3.dist-info → punkweb_bb-0.2.0.dist-info}/METADATA +6 -2
  46. {punkweb_bb-0.1.3.dist-info → punkweb_bb-0.2.0.dist-info}/RECORD +49 -30
  47. {punkweb_bb-0.1.3.dist-info → punkweb_bb-0.2.0.dist-info}/LICENSE +0 -0
  48. {punkweb_bb-0.1.3.dist-info → punkweb_bb-0.2.0.dist-info}/WHEEL +0 -0
  49. {punkweb_bb-0.1.3.dist-info → punkweb_bb-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,19 @@
1
- {% load shoutbox_bbcode %}
1
+ {% load shoutbox_bbcode can_delete %}
2
2
 
3
3
  {% for shout in shouts %}
4
4
  <div class="shoutbox__shout">
5
- <span>{{shout.created_at|date:'g:i A'}}</span>
6
- <span><a href="{% url 'punkweb_bb:profile' shout.user.id %}">{{shout.user.username}}</a>: </span>
7
- <span>{{ shout.content | safe | shoutbox_bbcode }}</span>
5
+ <div class="shoutbox__shout__content">
6
+ <span>{{shout.created_at|date:'g:i A'}}</span>
7
+ <span><a href="{% url 'punkweb_bb:profile' shout.user.id %}">{{shout.user.username}}</a>: </span>
8
+ <span>{{ shout.content | safe | shoutbox_bbcode }}</span>
9
+ </div>
10
+ {% if shout|can_delete:request.user %}
11
+ <div class="shoutbox__shout__actions">
12
+ <button class="pw-icon-button danger xs" hx-get="{% url 'punkweb_bb:shout_delete' shout.id %}"
13
+ hx-target="#modal-portal">
14
+ <span class="material-symbols-outlined">close</span>
15
+ </button>
16
+ </div>
17
+ {% endif %}
8
18
  </div>
9
19
  {% endfor %}
@@ -1,5 +1,5 @@
1
1
  {% extends 'punkweb_bb/base.html' %}
2
- {% load static humanize_int %}
2
+ {% load static humanize_int can_post %}
3
3
 
4
4
  {% block title_prefix %}{{subcategory.name}} | {% endblock%}
5
5
 
@@ -24,21 +24,36 @@
24
24
  </nav>
25
25
 
26
26
  <div class="subcategory__header">
27
- <h1>{{subcategory.name}}</h1>
28
- {% if request.user.is_authenticated %}
29
- {% if not subcategory.staff_post_only or request.user.is_staff %}
27
+ <div class="subcategory__header__name">
28
+ <h1>{{subcategory.name}}</h1>
29
+ {% if perms.punkweb_bb.change_subcategory or perms.punkweb_bb.delete_subcategory %}
30
+ <div class="subcategory__header__actions">
31
+ {% if perms.punkweb_bb.change_subcategory %}
32
+ <a class="pw-button ghost xs" href="{% url 'punkweb_bb:subcategory_update' subcategory.slug %}">
33
+ Edit
34
+ </a>
35
+ {% endif %}
36
+ {% if perms.punkweb_bb.delete_subcategory %}
37
+ <a class="pw-button ghost danger xs" hx-get="{% url 'punkweb_bb:subcategory_delete' subcategory.slug %}" hx-target="#modal-portal">
38
+ Delete
39
+ </a>
40
+ {% endif %}
41
+ </div>
42
+ {% endif %}
43
+ </div>
44
+
45
+ {% if subcategory|can_post:request.user %}
30
46
  <a class="pw-button ghost primary" href="{% url 'punkweb_bb:thread_create' subcategory.slug %}">Create Thread</a>
31
47
  {% else %}
32
48
  <button
33
49
  class="pw-button ghost primary"
34
50
  disabled
35
51
  href="{% url 'punkweb_bb:thread_create' subcategory.slug %}"
36
- title="Only staff can create threads in this subcategory"
52
+ title="You do not have permission to create a thread in this subcategory."
37
53
  >
38
54
  Create Thread
39
55
  </button>
40
56
  {% endif %}
41
- {% endif %}
42
57
  </div>
43
58
 
44
59
  <div class="pw-card fluid margin">
@@ -0,0 +1,53 @@
1
+ {% extends 'punkweb_bb/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title_prefix %}Create Subcategory | {% endblock %}
5
+
6
+ {% block extra_head %}
7
+ {{form.media.css}}
8
+ <link rel="stylesheet" href="{% static 'punkweb_bb/css/subcategory-form.css' %}" />
9
+ {% endblock %}
10
+
11
+ {% block content %}
12
+
13
+ <nav>
14
+ <ul class="pw-breadcrumb">
15
+ <li class="pw-breadcrumb-item">
16
+ <a href="{% url 'punkweb_bb:index' %}">Home</a>
17
+ </li>
18
+ <li class="pw-breadcrumb-item">
19
+ <a href="{{category.get_absolute_url}}">{{category.name}}</a>
20
+ </li>
21
+ <li class="pw-breadcrumb-item active">
22
+ Create Subcategory
23
+ </li>
24
+ </ul>
25
+ </nav>
26
+
27
+ <div class="pw-card fluid">
28
+ <div class="pw-card-header">Create Subcategory</div>
29
+ <div class="pw-card-content">
30
+ <form class="subcategoryForm" action="{% url 'punkweb_bb:subcategory_create' category.slug %}" method="post">
31
+ {% csrf_token %}
32
+ {% for field in form %}
33
+ <div class="subcategoryForm__field">
34
+ <label class="pw-input-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
35
+ {{ field }}
36
+ </div>
37
+ {% endfor %}
38
+ {% if form.non_field_errors %}
39
+ {{ form.non_field_errors }}
40
+ {% endif %}
41
+ <div class="subcategoryForm__actions">
42
+ <a class="pw-button default" href="{% url 'punkweb_bb:index' %}" type="submit">Cancel</a>
43
+ <button class="pw-button raised primary" type="submit">Create</button>
44
+ </div>
45
+ </form>
46
+ </div>
47
+ </div>
48
+
49
+ {% endblock %}
50
+
51
+ {% block extra_script %}
52
+ {{form.media.js}}
53
+ {% endblock %}
@@ -0,0 +1,56 @@
1
+ {% extends 'punkweb_bb/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title_prefix %}Edit Subcategory | {% endblock %}
5
+
6
+ {% block extra_head %}
7
+ {{form.media.css}}
8
+ <link rel="stylesheet" href="{% static 'punkweb_bb/css/subcategory-form.css' %}" />
9
+ {% endblock %}
10
+
11
+ {% block content %}
12
+
13
+ <nav>
14
+ <ul class="pw-breadcrumb">
15
+ <li class="pw-breadcrumb-item">
16
+ <a href="{% url 'punkweb_bb:index' %}">Home</a>
17
+ </li>
18
+ <li class="pw-breadcrumb-item">
19
+ <a href="{{subcategory.category.get_absolute_url}}">{{subcategory.category.name}}</a>
20
+ </li>
21
+ <li class="pw-breadcrumb-item">
22
+ <a href="{{subcategory.get_absolute_url}}">{{subcategory.name}}</a>
23
+ </li>
24
+ <li class="pw-breadcrumb-item active">
25
+ Edit
26
+ </li>
27
+ </ul>
28
+ </nav>
29
+
30
+ <div class="pw-card fluid">
31
+ <div class="pw-card-header">Edit Subcategory</div>
32
+ <div class="pw-card-content">
33
+ <form class="subcategoryForm" action="{% url 'punkweb_bb:subcategory_update' subcategory.slug %}" method="post">
34
+ {% csrf_token %}
35
+ {% for field in form %}
36
+ <div class="subcategoryForm__field">
37
+ <label class="pw-input-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
38
+ {{ field }}
39
+ </div>
40
+ {% endfor %}
41
+ {% if form.non_field_errors %}
42
+ {{ form.non_field_errors }}
43
+ {% endif %}
44
+ <div class="subcategoryForm__actions">
45
+ <a class="pw-button default" href="{{subcategory.get_absolute_url}}">Cancel</a>
46
+ <button class="pw-button raised primary" type="submit">Save</button>
47
+ </div>
48
+ </form>
49
+ </div>
50
+ </div>
51
+
52
+ {% endblock %}
53
+
54
+ {% block extra_script %}
55
+ {{form.media.js}}
56
+ {% endblock %}
@@ -1,5 +1,5 @@
1
1
  {% extends 'punkweb_bb/base.html' %}
2
- {% load static %}
2
+ {% load static can_delete can_edit can_post %}
3
3
 
4
4
  {% block title_prefix %}{{thread.title}} | {% endblock %}
5
5
 
@@ -67,6 +67,13 @@
67
67
  <div class="thread__user__info__value">{{thread.user.profile.post_count}}</div>
68
68
  </div>
69
69
  </div>
70
+ {% if thread.user.groups.all|length > 0 %}
71
+ <div class="thread__user__groups">
72
+ {% for group in thread.user.groups.all %}
73
+ <div class="thread__user__group">{{group.name}}</div>
74
+ {% endfor %}
75
+ </div>
76
+ {% endif %}
70
77
  </div>
71
78
  <div class="thread__main">
72
79
  <div class="thread__content">
@@ -77,13 +84,17 @@
77
84
  {{thread.user.profile.signature.rendered}}
78
85
  </div>
79
86
  {% endif %}
80
- {% if thread.user == request.user %}
87
+ {% if thread|can_delete:request.user or thread|can_edit:request.user %}
81
88
  <div class="thread__actions">
89
+ {% if thread|can_edit:request.user %}
82
90
  <a class="pw-button ghost sm" href="{% url 'punkweb_bb:thread_update' thread.id %}">Edit</a>
91
+ {% endif %}
92
+ {% if thread|can_delete:request.user %}
83
93
  <a class="pw-button ghost danger sm" hx-get="{% url 'punkweb_bb:thread_delete' thread.id %}"
84
94
  hx-target="#modal-portal">
85
95
  Delete
86
96
  </a>
97
+ {% endif %}
87
98
  </div>
88
99
  {% endif %}
89
100
  </div>
@@ -128,6 +139,13 @@
128
139
  <div class="thread__user__info__value">{{post.user.profile.post_count}}</div>
129
140
  </div>
130
141
  </div>
142
+ {% if post.user.groups.all|length > 0 %}
143
+ <div class="thread__user__groups">
144
+ {% for group in post.user.groups.all %}
145
+ <div class="thread__user__group">{{group.name}}</div>
146
+ {% endfor %}
147
+ </div>
148
+ {% endif %}
131
149
  </div>
132
150
  <div class="thread__main">
133
151
  <div class="thread__content">
@@ -138,13 +156,17 @@
138
156
  {{post.user.profile.signature.rendered}}
139
157
  </div>
140
158
  {% endif %}
141
- {% if post.user == request.user %}
159
+ {% if post|can_delete:request.user or post|can_edit:request.user %}
142
160
  <div class="thread__actions">
161
+ {% if post|can_edit:request.user %}
143
162
  <a class="pw-button ghost sm" hx-get="{% url 'punkweb_bb:post_update' post.id %}" hx-target="#modal-portal">Edit</a>
163
+ {% endif %}
164
+ {% if post|can_delete:request.user %}
144
165
  <a class="pw-button ghost danger sm" hx-get="{% url 'punkweb_bb:post_delete' post.id %}"
145
166
  hx-target="#modal-portal">
146
167
  Delete
147
168
  </a>
169
+ {% endif %}
148
170
  </div>
149
171
  {% endif %}
150
172
  </div>
@@ -198,8 +220,7 @@
198
220
  </nav>
199
221
  {% endif %}
200
222
 
201
- {% if request.user.is_authenticated %}
202
- {% if not thread.is_closed %}
223
+ {% if thread|can_post:request.user %}
203
224
  <div class="pw-card fluid margin">
204
225
  <div class="pw-card-header">Reply to thread</div>
205
226
  <div class="pw-card-content">
@@ -229,6 +250,5 @@
229
250
  </div>
230
251
  </div>
231
252
  {% endif %}
232
- {% endif %}
233
253
 
234
254
  {% endblock %}
@@ -42,6 +42,7 @@
42
42
  {{ form.non_field_errors }}
43
43
  {% endif %}
44
44
  <div class="threadForm__actions">
45
+ <a class="pw-button default" href="{{subcategory.get_absolute_url}}" type="submit">Cancel</a>
45
46
  <button class="pw-button raised primary" type="submit">Create Thread</button>
46
47
  </div>
47
48
  </form>
@@ -0,0 +1,8 @@
1
+ from django import template
2
+
3
+ register = template.Library()
4
+
5
+
6
+ @register.filter
7
+ def can_delete(obj, user):
8
+ return obj.can_delete(user)
@@ -0,0 +1,8 @@
1
+ from django import template
2
+
3
+ register = template.Library()
4
+
5
+
6
+ @register.filter
7
+ def can_edit(obj, user):
8
+ return obj.can_edit(user)
@@ -0,0 +1,8 @@
1
+ from django import template
2
+
3
+ register = template.Library()
4
+
5
+
6
+ @register.filter
7
+ def can_post(obj, user):
8
+ return obj.can_post(user)
punkweb_bb/tests.py CHANGED
@@ -299,14 +299,14 @@ class IndexViewTestCase(TestCase):
299
299
  username="staff", password="staff", is_staff=True
300
300
  )
301
301
 
302
- def test_users_online(self):
302
+ def test_members_online(self):
303
303
  response = self.client.get(self.url)
304
- self.assertEqual(len(response.context["users_online"]), 0)
304
+ self.assertEqual(len(response.context["members_online"]), 0)
305
305
 
306
306
  self.client.force_login(self.user)
307
307
  response = self.client.get(self.url)
308
308
 
309
- self.assertEqual(len(response.context["users_online"]), 1)
309
+ self.assertEqual(len(response.context["members_online"]), 1)
310
310
 
311
311
  def test_staff_online(self):
312
312
  response = self.client.get(self.url)
@@ -317,6 +317,17 @@ class IndexViewTestCase(TestCase):
317
317
 
318
318
  self.assertEqual(len(response.context["staff_online"]), 1)
319
319
 
320
+ def test_guests_online(self):
321
+ response = self.client.get(self.url)
322
+ self.assertEqual(response.context["guests_online"], 1)
323
+
324
+ def test_total_online(self):
325
+ response = self.client.get(self.url)
326
+ self.client.force_login(self.user)
327
+ response = self.client.get(self.url)
328
+
329
+ self.assertEqual(response.context["total_online"], 2)
330
+
320
331
  def test_newest_user(self):
321
332
  response = self.client.get(self.url)
322
333
  self.assertEqual(response.context["newest_user"], self.staff_user)
@@ -463,7 +474,7 @@ class ThreadCreateViewTestCase(TestCase):
463
474
  self.client.force_login(self.user)
464
475
  response = self.client.get(self.staff_only_url)
465
476
 
466
- self.assertRedirects(response, self.staff_subcategory.get_absolute_url())
477
+ self.assertEqual(response.status_code, 403)
467
478
 
468
479
 
469
480
  class ThreadViewTestCase(TestCase):
@@ -511,7 +522,7 @@ class ThreadUpdateViewTestCase(TestCase):
511
522
  def test_is_author(self):
512
523
  self.client.force_login(self.other_user)
513
524
  response = self.client.get(self.url)
514
- self.assertEqual(response.status_code, 404)
525
+ self.assertEqual(response.status_code, 403)
515
526
 
516
527
  self.client.force_login(self.user)
517
528
  response = self.client.get(self.url)
@@ -560,7 +571,7 @@ class ThreadDeleteViewTestCase(TestCase):
560
571
  def test_is_author(self):
561
572
  self.client.force_login(self.other_user)
562
573
  response = self.client.get(self.url)
563
- self.assertEqual(response.status_code, 404)
574
+ self.assertEqual(response.status_code, 403)
564
575
 
565
576
  self.client.force_login(self.user)
566
577
  response = self.client.get(self.url)
@@ -642,7 +653,7 @@ class PostUpdateViewTestCase(TestCase):
642
653
  def test_is_author(self):
643
654
  self.client.force_login(self.other_user)
644
655
  response = self.client.get(self.url)
645
- self.assertEqual(response.status_code, 404)
656
+ self.assertEqual(response.status_code, 403)
646
657
 
647
658
  self.client.force_login(self.user)
648
659
  response = self.client.get(self.url)
@@ -692,7 +703,7 @@ class PostDeleteViewTestCase(TestCase):
692
703
  def test_is_author(self):
693
704
  self.client.force_login(self.other_user)
694
705
  response = self.client.get(self.url)
695
- self.assertEqual(response.status_code, 404)
706
+ self.assertEqual(response.status_code, 403)
696
707
 
697
708
  self.client.force_login(self.user)
698
709
  response = self.client.get(self.url)
punkweb_bb/urls.py CHANGED
@@ -11,11 +11,37 @@ urlpatterns = [
11
11
  path("settings/", views.settings_view, name="settings"),
12
12
  path("members/", views.members_view, name="members"),
13
13
  path("members/<path:user_id>/", views.profile_view, name="profile"),
14
+ path("create-category/", views.category_create_view, name="category_create"),
15
+ path(
16
+ "category/<str:category_slug>/update/",
17
+ views.category_update_view,
18
+ name="category_update",
19
+ ),
20
+ path(
21
+ "category/<str:category_slug>/delete/",
22
+ views.category_delete_view,
23
+ name="category_delete",
24
+ ),
14
25
  path(
15
26
  "subcategory/<str:subcategory_slug>/",
16
27
  views.subcategory_view,
17
28
  name="subcategory",
18
29
  ),
30
+ path(
31
+ "category/<str:category_slug>/create-subcategory/",
32
+ views.subcategory_create_view,
33
+ name="subcategory_create",
34
+ ),
35
+ path(
36
+ "subcategory/<str:subcategory_slug>/update/",
37
+ views.subcategory_update_view,
38
+ name="subcategory_update",
39
+ ),
40
+ path(
41
+ "subcategory/<str:subcategory_slug>/delete/",
42
+ views.subcategory_delete_view,
43
+ name="subcategory_delete",
44
+ ),
19
45
  path(
20
46
  "subcategory/<str:subcategory_slug>/create-thread/",
21
47
  views.thread_create_view,
@@ -37,5 +63,6 @@ urlpatterns = [
37
63
  path("post/<str:post_id>/update/", views.post_update_view, name="post_update"),
38
64
  path("shout-list/", views.shout_list_view, name="shout_list"),
39
65
  path("shout-create/", views.shout_create_view, name="shout_create"),
66
+ path("shout/<str:shout_id>/delete/", views.shout_delete_view, name="shout_delete"),
40
67
  path("bbcode/", views.bbcode_view, name="bbcode"),
41
68
  ]
punkweb_bb/utils.py ADDED
@@ -0,0 +1,17 @@
1
+ from django.utils.text import slugify
2
+
3
+
4
+ def get_unique_slug(model, field):
5
+ base_slug = slugify(field)
6
+ slug = base_slug
7
+ unique_slug_exists = True
8
+
9
+ counter = 1
10
+ while unique_slug_exists:
11
+ if model.objects.filter(slug=slug).exists():
12
+ slug = f"{base_slug}-{counter}"
13
+ counter += 1
14
+ else:
15
+ unique_slug_exists = False
16
+
17
+ return slug