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
Binary file
punkweb_bb/forms.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from django import forms
2
2
  from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
3
3
 
4
- from punkweb_bb.models import BoardProfile, Post, Shout, Thread
4
+ from punkweb_bb.models import BoardProfile, Category, Post, Shout, Subcategory, Thread
5
5
  from punkweb_bb.widgets import BBCodeEditorWidget
6
6
 
7
7
 
@@ -43,6 +43,21 @@ class BoardProfileModelForm(forms.ModelForm):
43
43
  }
44
44
 
45
45
 
46
+ class CategoryModelForm(forms.ModelForm):
47
+ class Meta:
48
+ model = Category
49
+ fields = (
50
+ "name",
51
+ "order",
52
+ )
53
+ widgets = {
54
+ "name": forms.TextInput(attrs={"autofocus": True, "class": "pw-input"}),
55
+ "order": forms.TextInput(
56
+ attrs={"class": "pw-input", "min": "0", "type": "number"}
57
+ ),
58
+ }
59
+
60
+
46
61
  class PostModelForm(forms.ModelForm):
47
62
  class Meta:
48
63
  model = Post
@@ -55,6 +70,30 @@ class PostModelForm(forms.ModelForm):
55
70
  }
56
71
 
57
72
 
73
+ class ShoutModelForm(forms.ModelForm):
74
+ class Meta:
75
+ model = Shout
76
+ fields = ("content",)
77
+
78
+
79
+ class SubcategoryModelForm(forms.ModelForm):
80
+ class Meta:
81
+ model = Subcategory
82
+ fields = (
83
+ "name",
84
+ "description",
85
+ "order",
86
+ "staff_post_only",
87
+ )
88
+ widgets = {
89
+ "name": forms.TextInput(attrs={"autofocus": True, "class": "pw-input"}),
90
+ "description": BBCodeEditorWidget(),
91
+ "order": forms.TextInput(
92
+ attrs={"class": "pw-input", "min": "0", "type": "number"}
93
+ ),
94
+ }
95
+
96
+
58
97
  class ThreadModelForm(forms.ModelForm):
59
98
  class Meta:
60
99
  model = Thread
@@ -66,9 +105,3 @@ class ThreadModelForm(forms.ModelForm):
66
105
  "title": forms.TextInput(attrs={"autofocus": True, "class": "pw-input"}),
67
106
  "content": BBCodeEditorWidget(),
68
107
  }
69
-
70
-
71
- class ShoutModelForm(forms.ModelForm):
72
- class Meta:
73
- model = Shout
74
- fields = ("content",)
punkweb_bb/guests.py ADDED
@@ -0,0 +1,20 @@
1
+ from expiringdict import ExpiringDict
2
+
3
+
4
+ class GuestList:
5
+ def __init__(self):
6
+ self.__guests = ExpiringDict(max_len=2048, max_age_seconds=60 * 5)
7
+
8
+ def length(self):
9
+ return len(self.__guests)
10
+
11
+ def add(self, ip):
12
+ if not self.__guests.get(ip, None):
13
+ self.__guests[ip] = True
14
+
15
+ def clear_expired(self):
16
+ for key in list(self.__guests.keys()):
17
+ _ = self.__guests.get(key, None)
18
+
19
+
20
+ guest_list = GuestList()
punkweb_bb/middleware.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from django.core.cache import cache
2
2
  from django.utils import timezone
3
3
 
4
+ from punkweb_bb.guests import guest_list
5
+
4
6
 
5
7
  class ProfileOnlineCacheMiddleware:
6
8
  def __init__(self, get_response):
@@ -11,6 +13,11 @@ class ProfileOnlineCacheMiddleware:
11
13
  cache.set(
12
14
  f"profile_online_{request.user.profile.id}", timezone.now(), 60 * 5
13
15
  )
16
+ else:
17
+ ip = request.META.get("REMOTE_ADDR")
18
+ guest_list.add(ip)
19
+
20
+ guest_list.clear_expired()
14
21
 
15
22
  response = self.get_response(request)
16
23
 
punkweb_bb/models.py CHANGED
@@ -79,6 +79,9 @@ class Subcategory(UUIDPrimaryKeyMixin, TimestampMixin):
79
79
  "order",
80
80
  )
81
81
 
82
+ def can_post(self, user):
83
+ return not self.staff_post_only or user.is_staff
84
+
82
85
  @property
83
86
  def thread_count(self):
84
87
  return self.threads.count()
@@ -120,6 +123,15 @@ class Thread(UUIDPrimaryKeyMixin, TimestampMixin):
120
123
  def __str__(self):
121
124
  return f"{self.title}"
122
125
 
126
+ def can_edit(self, user):
127
+ return user == self.user or user.has_perm("punkweb_bb.change_thread")
128
+
129
+ def can_delete(self, user):
130
+ return user == self.user or user.has_perm("punkweb_bb.delete_thread")
131
+
132
+ def can_post(self, user):
133
+ return not self.is_closed
134
+
123
135
  @property
124
136
  def post_count(self):
125
137
  return self.posts.count()
@@ -143,6 +155,12 @@ class Post(UUIDPrimaryKeyMixin, TimestampMixin):
143
155
  def __str__(self):
144
156
  return f"{self.thread} > {self.user} > {self.created_at}"
145
157
 
158
+ def can_edit(self, user):
159
+ return user == self.user or user.has_perm("punkweb_bb.change_post")
160
+
161
+ def can_delete(self, user):
162
+ return user == self.user or user.has_perm("punkweb_bb.delete_post")
163
+
146
164
  @property
147
165
  def index(self):
148
166
  # Returns the index of the post in the thread, starting with 1
@@ -180,3 +198,6 @@ class Shout(UUIDPrimaryKeyMixin, TimestampMixin):
180
198
 
181
199
  def __str__(self):
182
200
  return f"{self.user} > {self.created_at}"
201
+
202
+ def can_delete(self, user):
203
+ return user == self.user or user.has_perm("punkweb_bb.delete_shout")
punkweb_bb/settings.py CHANGED
@@ -6,3 +6,6 @@ SITE_NAME = PUNKWEB_BB.get("SITE_NAME", "PUNKWEB")
6
6
  SITE_TITLE = PUNKWEB_BB.get("SITE_TITLE", "PunkwebBB")
7
7
  FAVICON = PUNKWEB_BB.get("FAVICON", "punkweb_bb/favicon.ico")
8
8
  SHOUTBOX_ENABLED = PUNKWEB_BB.get("SHOUTBOX_ENABLED", True)
9
+ DISCORD_WIDGET_ENABLED = PUNKWEB_BB.get("DISCORD_WIDGET_ENABLED", False)
10
+ DISCORD_WIDGET_THEME = PUNKWEB_BB.get("DISCORD_WIDGET_THEME", "dark")
11
+ DISCORD_SERVER_ID = PUNKWEB_BB.get("DISCORD_SERVER_ID", None)
@@ -0,0 +1,24 @@
1
+ .categoryForm {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ }
6
+
7
+ .categoryForm__field {
8
+ align-items: start;
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 0.5rem;
12
+ }
13
+
14
+ .categoryForm .errorlist {
15
+ color: var(--oc-red-9);
16
+ margin: 0;
17
+ }
18
+
19
+ .categoryForm__actions {
20
+ align-items: center;
21
+ display: flex;
22
+ justify-content: flex-end;
23
+ gap: 1rem;
24
+ }
@@ -7,6 +7,12 @@
7
7
  flex: 1;
8
8
  }
9
9
 
10
+ .index__category__actions {
11
+ align-items: center;
12
+ display: flex;
13
+ gap: 0.25rem;
14
+ }
15
+
10
16
  .index__sidebar {
11
17
  flex: 0 0 20rem;
12
18
  }
@@ -406,6 +406,67 @@ blockquote cite {
406
406
  width: 2rem;
407
407
  }
408
408
 
409
+ .pw-icon-button .material-symbols-outlined {
410
+ display: block;
411
+ font-size: 1.125rem;
412
+ }
413
+
414
+ .pw-icon-button.xs {
415
+ height: 1rem;
416
+ line-height: 1rem;
417
+ max-height: 1rem;
418
+ max-width: 1rem;
419
+ min-height: 1rem;
420
+ min-width: 1rem;
421
+ width: 1rem;
422
+ }
423
+
424
+ .pw-icon-button.xs .material-symbols-outlined {
425
+ font-size: 0.875rem;
426
+ }
427
+
428
+ .pw-icon-button.sm {
429
+ height: 1.5rem;
430
+ line-height: 1.5rem;
431
+ max-height: 1.5rem;
432
+ max-width: 1.5rem;
433
+ min-height: 1.5rem;
434
+ min-width: 1.5rem;
435
+ width: 1.5rem;
436
+ }
437
+
438
+ .pw-icon-button.sm .material-symbols-outlined {
439
+ font-size: 1rem;
440
+ }
441
+
442
+ .pw-icon-button.lg {
443
+ height: 2.5rem;
444
+ line-height: 2.5rem;
445
+ max-height: 2.5rem;
446
+ max-width: 2.5rem;
447
+ min-height: 2.5rem;
448
+ min-width: 2.5rem;
449
+ width: 2.5rem;
450
+ }
451
+
452
+ .pw-icon-button.lg .material-symbols-outlined {
453
+ font-size: 1.25rem;
454
+ }
455
+
456
+ .pw-icon-button.xl {
457
+ height: 3rem;
458
+ line-height: 3rem;
459
+ max-height: 3rem;
460
+ max-width: 3rem;
461
+ min-height: 3rem;
462
+ min-width: 3rem;
463
+ width: 3rem;
464
+ }
465
+
466
+ .pw-icon-button.sl .material-symbols-outlined {
467
+ font-size: 1.5rem;
468
+ }
469
+
409
470
  .pw-icon-button:disabled {
410
471
  background-color: var(--oc-gray-5) !important;
411
472
  border-color: var(--oc-gray-5) !important;
@@ -413,11 +474,6 @@ blockquote cite {
413
474
  cursor: not-allowed;
414
475
  }
415
476
 
416
- .pw-icon-button .material-symbols-outlined {
417
- display: block;
418
- font-size: 1.125rem;
419
- }
420
-
421
477
  .pw-icon-button.rounded {
422
478
  border-radius: 100%;
423
479
  }
@@ -646,3 +702,26 @@ blockquote cite {
646
702
  .pw-breadcrumb-item.active {
647
703
  color: var(--breadcrumb-active);
648
704
  }
705
+
706
+ /* Callout */
707
+
708
+ .pw-callout {
709
+ background-color: var(--oc-gray-1);
710
+ border: 1px solid var(--border);
711
+ border-radius: 0.25rem;
712
+ color: var(--oc-gray-9);
713
+ padding: 0.5rem 1rem;
714
+ width: 100%;
715
+ }
716
+
717
+ .pw-callout.primary {
718
+ background-color: var(--primary-0);
719
+ border-color: 1px solid var(--primary-4);
720
+ color: var(--primary-9);
721
+ }
722
+
723
+ .pw-callout.danger {
724
+ background-color: var(--oc-red-0);
725
+ border-color: 1px solid var(--oc-red-4);
726
+ color: var(--oc-red-9);
727
+ }
@@ -3,16 +3,27 @@
3
3
  display: flex;
4
4
  flex-direction: column;
5
5
  height: 12rem;
6
- gap: 0.25rem;
7
6
  overflow-y: auto;
8
- padding: 1rem;
9
7
  }
10
8
 
11
9
  .shoutbox__shout {
10
+ align-items: center;
11
+ display: flex;
12
+ justify-content: space-between;
12
13
  font-size: 0.75rem;
14
+ padding: 0.25rem 1rem;
13
15
  white-space: wrap;
14
16
  }
15
17
 
18
+ .shoutbox__shout:hover {
19
+ background-color: var(--oc-gray-1);
20
+ }
21
+
22
+ .shoutbox__shout__actions {
23
+ align-items: center;
24
+ display: flex;
25
+ }
26
+
16
27
  .shoutbox__form {
17
28
  padding: 0.5rem 0;
18
29
  }
@@ -0,0 +1,24 @@
1
+ .subcategoryForm {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ }
6
+
7
+ .subcategoryForm__field {
8
+ align-items: start;
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 0.5rem;
12
+ }
13
+
14
+ .subcategoryForm .errorlist {
15
+ color: var(--oc-red-9);
16
+ margin: 0;
17
+ }
18
+
19
+ .subcategoryForm__actions {
20
+ align-items: center;
21
+ display: flex;
22
+ justify-content: flex-end;
23
+ gap: 1rem;
24
+ }
@@ -5,6 +5,18 @@
5
5
  margin: 2rem 0;
6
6
  }
7
7
 
8
+ .subcategory__header__name {
9
+ align-items: center;
10
+ display: flex;
11
+ gap: 1rem;
12
+ }
13
+
14
+ .subcategory__header__actions {
15
+ align-items: center;
16
+ display: flex;
17
+ gap: 0.5rem;
18
+ }
19
+
8
20
  .thread__title {
9
21
  align-items: center;
10
22
  display: flex;
@@ -55,6 +55,25 @@
55
55
  color: var(--body-fg);
56
56
  }
57
57
 
58
+ .thread__user__groups {
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: 0.5rem;
62
+ width: 100%;
63
+ }
64
+
65
+ .thread__user__group {
66
+ background-color: var(--primary-8);
67
+ border: 1px solid var(--primary-9);
68
+ border-radius: 0.25rem;
69
+ color: var(--text-on-primary);
70
+ display: flex;
71
+ font-size: 0.875rem;
72
+ justify-content: center;
73
+ padding: 0.5rem;
74
+ width: 100%;
75
+ }
76
+
58
77
  .thread__main {
59
78
  display: flex;
60
79
  flex-direction: column;
@@ -12,6 +12,7 @@
12
12
 
13
13
  --text-dark: var(--oc-gray-9);
14
14
  --text-light: var(--oc-gray-0);
15
+ --text-on-primary: var(--oc-gray-0);
15
16
 
16
17
  --body-bg: var(--oc-white);
17
18
  --body-fg: var(--text-dark);
@@ -0,0 +1,50 @@
1
+ {% extends 'punkweb_bb/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title_prefix %}Create Category | {% endblock %}
5
+
6
+ {% block extra_head %}
7
+ {{form.media.css}}
8
+ <link rel="stylesheet" href="{% static 'punkweb_bb/css/category-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 active">
19
+ Create Category
20
+ </li>
21
+ </ul>
22
+ </nav>
23
+
24
+ <div class="pw-card fluid">
25
+ <div class="pw-card-header">Create Category</div>
26
+ <div class="pw-card-content">
27
+ <form class="categoryForm" action="{% url 'punkweb_bb:category_create' %}" method="post">
28
+ {% csrf_token %}
29
+ {% for field in form %}
30
+ <div class="categoryForm__field">
31
+ <label class="pw-input-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
32
+ {{ field }}
33
+ </div>
34
+ {% endfor %}
35
+ {% if form.non_field_errors %}
36
+ {{ form.non_field_errors }}
37
+ {% endif %}
38
+ <div class="categoryForm__actions">
39
+ <a class="pw-button default" href="{% url 'punkweb_bb:index' %}" type="submit">Cancel</a>
40
+ <button class="pw-button raised primary" type="submit">Create</button>
41
+ </div>
42
+ </form>
43
+ </div>
44
+ </div>
45
+
46
+ {% endblock %}
47
+
48
+ {% block extra_script %}
49
+ {{form.media.js}}
50
+ {% endblock %}
@@ -0,0 +1,53 @@
1
+ {% extends 'punkweb_bb/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title_prefix %}Edit Category | {% endblock %}
5
+
6
+ {% block extra_head %}
7
+ {{form.media.css}}
8
+ <link rel="stylesheet" href="{% static 'punkweb_bb/css/category-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
+ Edit
23
+ </li>
24
+ </ul>
25
+ </nav>
26
+
27
+ <div class="pw-card fluid">
28
+ <div class="pw-card-header">Edit Category</div>
29
+ <div class="pw-card-content">
30
+ <form class="categoryForm" action="{% url 'punkweb_bb:category_update' category.slug %}" method="post">
31
+ {% csrf_token %}
32
+ {% for field in form %}
33
+ <div class="categoryForm__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="categoryForm__actions">
42
+ <a class="pw-button default" href="{{category.get_absolute_url}}">Cancel</a>
43
+ <button class="pw-button raised primary" type="submit">Save</button>
44
+ </div>
45
+ </form>
46
+ </div>
47
+ </div>
48
+
49
+ {% endblock %}
50
+
51
+ {% block extra_script %}
52
+ {{form.media.js}}
53
+ {% endblock %}
@@ -38,12 +38,35 @@
38
38
  </tr>
39
39
  </thead>
40
40
  <tbody>
41
+ {% if perms.punkweb_bb.add_subcategory or perms.punkweb_bb.change_category or perms.punkweb_bb.delete_category %}
42
+ <tr>
43
+ <td colspan="4">
44
+ <div class="index__category__actions">
45
+ {% if perms.punkweb_bb.add_subcategory %}
46
+ <a class="pw-button ghost xs" href="{% url 'punkweb_bb:subcategory_create' category.slug %}">
47
+ Create Subcategory
48
+ </a>
49
+ {% endif %}
50
+ {% if perms.punkweb_bb.change_category %}
51
+ <a class="pw-button ghost xs" href="{% url 'punkweb_bb:category_update' category.slug %}">
52
+ Edit
53
+ </a>
54
+ {% endif %}
55
+ {% if perms.punkweb_bb.delete_category %}
56
+ <a class="pw-button ghost danger xs" hx-get="{% url 'punkweb_bb:category_delete' category.slug %}" hx-target="#modal-portal">
57
+ Delete
58
+ </a>
59
+ {% endif %}
60
+ </div>
61
+ </td>
62
+ </tr>
63
+ {% endif %}
41
64
  {% for subcategory in category.subcategories.all %}
42
65
  <tr>
43
66
  <td>
44
67
  <a href="{{subcategory.get_absolute_url}}" title="{{subcategory.name}}">{{subcategory.name}}</a>
45
68
  <div>
46
- {{subcategory.description}}
69
+ {{subcategory.description.rendered}}
47
70
  </div>
48
71
  </td>
49
72
  <td>{{subcategory.thread_count}}</td>
@@ -110,6 +133,9 @@
110
133
  </div>
111
134
  </div>
112
135
  {% endfor %}
136
+ {% if perms.punkweb_bb.add_category %}
137
+ <a class="pw-button ghost xs" href="{% url 'punkweb_bb:category_create' %}">Create Category</a>
138
+ {% endif %}
113
139
  </div>
114
140
  <div class="index__sidebar">
115
141
  <div class="pw-card fluid">
@@ -171,16 +197,29 @@
171
197
  </div>
172
198
  {% endif %}
173
199
  <div class="index__statistics__row">
174
- <div class="index__statistics__label">Members online:</div>
175
- <div class="index__statistics__value">{{users_online|length}}</div>
176
- </div>
177
- <div class="index__statistics__row">
178
- <div class="index__statistics__label">Staff online:</div>
179
- <div class="index__statistics__value">{{staff_online|length}}</div>
200
+ <div class="index__statistics__label">Users online:</div>
201
+ <div class="index__statistics__value">{{total_online}} ({{members_online|length}} members, {{staff_online|length}} staff, {{guests_online}} guests)</div>
180
202
  </div>
181
203
  </div>
182
204
  </div>
183
205
  </div>
206
+ {% if punkweb_bb.settings.DISCORD_WIDGET_ENABLED %}
207
+ {% if punkweb_bb.settings.DISCORD_SERVER_ID %}
208
+ <iframe
209
+ src="https://discord.com/widget?id={{ punkweb_bb.settings.DISCORD_SERVER_ID }}&theme={{ punkweb_bb.settings.DISCORD_WIDGET_THEME|default:'dark' }}"
210
+ width="100%"
211
+ height="384"
212
+ allowtransparency="true"
213
+ frameborder="0"
214
+ sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts">
215
+ </iframe>
216
+ {% else %}
217
+ <div class="pw-callout danger">
218
+ <h4>Discord Widget Error</h4>
219
+ <p><code>DISCORD_SERVER_ID</code> not configured in settings module.</p>
220
+ </div>
221
+ {% endif %}
222
+ {% endif %}
184
223
  </div>
185
224
  </div>
186
225
  </div>
@@ -0,0 +1,11 @@
1
+ {% extends 'punkweb_bb/base_modal.html' %}
2
+
3
+ {% block title %}Delete Category{% endblock %}
4
+
5
+ {% block content %}
6
+ <p>Are you sure you want to delete this category? This action cannot be undone!</p>
7
+ <div class="modal__actions">
8
+ <button class="modal__cancel pw-button default">Cancel</button>
9
+ <button class="pw-button raised danger" hx-delete="{% url 'punkweb_bb:category_delete' category.slug %}">Delete</button>
10
+ </div>
11
+ {% endblock %}
@@ -0,0 +1,11 @@
1
+ {% extends 'punkweb_bb/base_modal.html' %}
2
+
3
+ {% block title %}Delete Shout{% endblock %}
4
+
5
+ {% block content %}
6
+ <p>Are you sure you want to delete this shout? This action cannot be undone!</p>
7
+ <div class="modal__actions">
8
+ <button class="modal__cancel pw-button default">Cancel</button>
9
+ <button class="pw-button raised danger" hx-delete="{% url 'punkweb_bb:shout_delete' shout.id %}">Delete</button>
10
+ </div>
11
+ {% endblock %}
@@ -0,0 +1,11 @@
1
+ {% extends 'punkweb_bb/base_modal.html' %}
2
+
3
+ {% block title %}Delete Subcategory{% endblock %}
4
+
5
+ {% block content %}
6
+ <p>Are you sure you want to delete this subcategory? This action cannot be undone!</p>
7
+ <div class="modal__actions">
8
+ <button class="modal__cancel pw-button default">Cancel</button>
9
+ <button class="pw-button raised danger" hx-delete="{% url 'punkweb_bb:subcategory_delete' subcategory.slug %}">Delete</button>
10
+ </div>
11
+ {% endblock %}