update
This commit is contained in:
parent
9e0ebd5764
commit
82f9bbfde6
19 changed files with 467 additions and 215 deletions
42
TODO.txt
42
TODO.txt
|
|
@ -1,14 +1,44 @@
|
||||||
|
v1.0
|
||||||
[x] login page
|
[x] login page
|
||||||
[x] logout
|
[x] logout
|
||||||
[ ] change password
|
|
||||||
[x] member profile update and avatar
|
[x] member profile update and avatar
|
||||||
[x] forum
|
[x] forum
|
||||||
[ ] view other's profiles, add links in forums
|
[x] anonymous prayerbox
|
||||||
|
[x] view other's profiles, add links in forums
|
||||||
[x] member type
|
[x] member type
|
||||||
|
[x] change password page
|
||||||
|
[x] users must be authed
|
||||||
|
[x] signup must redirect NOT auto grant users access, and MUST give them some info on what to expect
|
||||||
|
|
||||||
|
v1.1
|
||||||
|
[ ] polls
|
||||||
|
[ ] watch forum, get email notifications when someone replies
|
||||||
|
[ ] @ replies?
|
||||||
|
[ ] display if user is currently logged on when seeing their posts and their profile
|
||||||
|
[ ] show how many posts a user has made, flair for high amounts of posts?
|
||||||
|
[ ] chat room? IRC?
|
||||||
|
|
||||||
|
TO DISCUSS:
|
||||||
[ ] nav bar
|
[ ] nav bar
|
||||||
[ ] users must be authed
|
[ ] page that displays all users and links to their websites (this already exists on neko, but better to automate it)
|
||||||
[ ] signup must redirect NOT auto grant users access, and MUST give them some info on what to expect
|
[ ] Pick better anonymous avatar
|
||||||
[ ] determine where which pages should live where
|
[ ] determine where which pages should live where
|
||||||
[ ] easier way to handle sites var, API endpoint?
|
[ ] easier way to handle sites var, API endpoint?
|
||||||
[ ] send out emails to existing users letting them know they have a password and to change it
|
|
||||||
[ ]
|
ACTION ITEMS:
|
||||||
|
DOMINIC ---
|
||||||
|
[x] join option without member area access
|
||||||
|
[x] NAV BAR
|
||||||
|
[x] list user api endpoint
|
||||||
|
[x] CSS
|
||||||
|
[x] Assets
|
||||||
|
[ ] soft-release
|
||||||
|
[ ] email notifications when user signs up: disroot email
|
||||||
|
[ ] maybe start sending emails to users automatically too
|
||||||
|
|
||||||
|
KYRIE ----
|
||||||
|
[ ] Welcome message
|
||||||
|
[ ] Signup screen
|
||||||
|
[ ] new css design
|
||||||
|
[ ] send out emails to existing users to sign up AFTER website is released
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,10 @@ USE_TZ = True
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
STATIC_ROOT = 'static/'
|
STATIC_ROOT = 'static/'
|
||||||
|
|
||||||
|
site_root = '/home/dominic/repos/cwr'
|
||||||
|
STATICFILES_DIRS = (
|
||||||
|
os.path.join(site_root, 'assets/'),
|
||||||
|
)
|
||||||
LOGIN_REDIRECT_URL = 'forum_threads'
|
LOGIN_REDIRECT_URL = 'forum_threads'
|
||||||
LOGOUT_REDIRECT_URL = "login"
|
LOGOUT_REDIRECT_URL = "login"
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
from web.views import signup, forum_threads, thread, custom_logout, profile
|
from web.views import signup, forum_threads, thread, custom_logout, profile, user_profile, denied, users
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('markdownx/', include('markdownx.urls')),
|
path('markdownx/', include('markdownx.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('api/users/', users, name='users'),
|
||||||
path("accounts/", include("django.contrib.auth.urls")),
|
path("accounts/", include("django.contrib.auth.urls")),
|
||||||
path("accounts/signup/", signup, name="signup"),
|
path("accounts/signup/", signup, name="signup"),
|
||||||
path("accounts/profile/", profile, name="profile"),
|
path("accounts/profile/", profile, name="profile"),
|
||||||
|
path("accounts/user/<user_id>", user_profile, name="user"),
|
||||||
|
path("accounts/denied/", denied, name="denied"),
|
||||||
path('logout/', custom_logout, name='custom_logout'),
|
path('logout/', custom_logout, name='custom_logout'),
|
||||||
|
path("", forum_threads, name='index'),
|
||||||
path("forum/threads/", forum_threads, name='forum_threads'),
|
path("forum/threads/", forum_threads, name='forum_threads'),
|
||||||
path("forum/thread/<thread_id>", thread, name='thread'),
|
path("forum/thread/<thread_id>", thread, name='thread'),
|
||||||
|
# path("test/", test_endpoint, name='test'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
BIN
static/img/bg.png
Normal file
BIN
static/img/bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
BIN
static/img/bible.png
Normal file
BIN
static/img/bible.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
BIN
static/img/widget-example.png
Normal file
BIN
static/img/widget-example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
19
web/forms.py
19
web/forms.py
|
|
@ -8,14 +8,22 @@ class SignupForm(UserCreationForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
for field_name in self.fields:
|
self.fields['email'].required = True
|
||||||
|
self.fields['email'].help_text = 'Email will be used to alert you of major announcements or information regarding your account.'
|
||||||
|
ignored_help_text_fields = ['password1', 'password2', 'username']
|
||||||
|
for field_name in ignored_help_text_fields:
|
||||||
self.fields[field_name].help_text = ''
|
self.fields[field_name].help_text = ''
|
||||||
|
|
||||||
url = forms.URLField(max_length=200, help_text='Required')
|
url = forms.URLField(max_length=200, required=True, help_text='The URL of the website that you intend to add the widget to. please follow the format of: https://your-website.com')
|
||||||
description = forms.CharField(widget=forms.Textarea)
|
description = forms.CharField(widget=forms.Textarea, help_text='Please provide a description about your website. Feel free to add a description about yourself as well.')
|
||||||
rule_conf = forms.BooleanField(label='Have you read the rules?')
|
|
||||||
|
rule_conf = forms.BooleanField(label='Have you read the rules?', help_text='Please be sure to read the rules before signing up for this webring. The rules can be found <a href="https://christian-webring.nekoweb.org/rules.html" target="_blank">here</a>')
|
||||||
rule_conf.widget.attrs.update({'class': 'checkmark'})
|
rule_conf.widget.attrs.update({'class': 'checkmark'})
|
||||||
comments = forms.CharField(widget=forms.Textarea)
|
|
||||||
|
email_only = forms.BooleanField(label='Skip Member Area', help_text='If you just want to add the webring widget to your website and do not care about the community aspect of this webring, click this checkmark. Any important announcements will be sent to you via email. If you decide you would like to join the member area later on, contact an admin.', required=False)
|
||||||
|
email_only.widget.attrs.update({'class': 'checkmark'})
|
||||||
|
|
||||||
|
comments = forms.CharField(widget=forms.Textarea, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomUser
|
model = CustomUser
|
||||||
|
|
@ -27,6 +35,7 @@ class SignupForm(UserCreationForm):
|
||||||
'url',
|
'url',
|
||||||
'description',
|
'description',
|
||||||
'rule_conf',
|
'rule_conf',
|
||||||
|
'email_only',
|
||||||
'comments'
|
'comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
18
web/migrations/0006_forumsubcategory_can_by_anon.py
Normal file
18
web/migrations/0006_forumsubcategory_can_by_anon.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.2.7 on 2025-10-23 18:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web', '0005_remove_forumpost_title_alter_forumpost_content'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='forumsubcategory',
|
||||||
|
name='can_by_anon',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 5.2.7 on 2025-11-01 01:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web', '0006_forumsubcategory_can_by_anon'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='email_only',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='comments',
|
||||||
|
field=models.TextField(blank=True, default=''),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='flair',
|
||||||
|
field=models.CharField(blank=True, default=''),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -7,6 +7,7 @@ class CustomUser(AbstractUser):
|
||||||
description = models.TextField(default='')
|
description = models.TextField(default='')
|
||||||
avatar = models.TextField(default='', blank=True)
|
avatar = models.TextField(default='', blank=True)
|
||||||
rule_conf = models.BooleanField(default=True)
|
rule_conf = models.BooleanField(default=True)
|
||||||
comments = models.TextField(default='')
|
comments = models.TextField(default='', blank=True)
|
||||||
flair = models.CharField(default='')
|
flair = models.CharField(default='', blank=True)
|
||||||
|
email_only = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ class ForumSubcategory(BaseModel):
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
forum_category = models.ForeignKey(ForumCategory, on_delete=models.CASCADE)
|
forum_category = models.ForeignKey(ForumCategory, on_delete=models.CASCADE)
|
||||||
sticky = models.BooleanField()
|
sticky = models.BooleanField()
|
||||||
|
can_by_anon = models.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = 'forum_subcategory'
|
db_table = 'forum_subcategory'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script async src="https://tally.so/widgets/embed.js"></script>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Christians of the Internet</title>
|
<title>Christians of the Internet</title>
|
||||||
|
|
@ -10,23 +10,26 @@
|
||||||
<body>
|
<body>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-color: #d7c5e0;
|
height: 100%;
|
||||||
background-image: url("https://sadhost.neocities.org/images/tiles/bgif016.gif");
|
background-color: #868F69;
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-family: "Cormorant Garamond", serif;
|
font-family: "Cormorant Garamond", serif;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
height: 100%;
|
background-image: url("{% static 'img/bg.png' %}");
|
||||||
}
|
}
|
||||||
|
|
||||||
h1.header {
|
h1.header {
|
||||||
font-family: "Special Elite", serif;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 45px;
|
margin: 30px 0;
|
||||||
font-weight: normal;
|
color: #603814;
|
||||||
color: #ffffff;
|
font-family: "Girassol", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 70px;
|
||||||
|
text-shadow: 1px 1px 0px #868F69, 2px 2px 0px #254117;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|
@ -44,6 +47,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: rgba(96,56,20, .9);
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
box-shadow: 0 5px 5px -2px #603814;
|
||||||
|
border-top-style: ridge;
|
||||||
|
border-left-style: ridge;
|
||||||
|
border-right-style: ridge;
|
||||||
|
border-color: #603814;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
|
|
@ -51,9 +63,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav a {
|
.nav a {
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
padding: 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin-right: 5px;
|
|
||||||
color: #657346;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav a:hover {
|
.nav a:hover {
|
||||||
|
|
@ -66,10 +79,25 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #3C241B;
|
||||||
|
padding: 15px;
|
||||||
|
text-align: justify;
|
||||||
|
/* HOW TO MAKE BOX SHADOW FOR TOP BORDER - A BIT JANKY BUT WORKS. Note how the one for the sides has completely transparent opacity. */
|
||||||
|
box-shadow: inset 0 -8px 4px 4px rgba(255, 255, 255, 0), inset 0 2px 4px 0px #603814;
|
||||||
|
border-left-style: ridge;
|
||||||
|
border-right-style: ridge;
|
||||||
|
border-bottom-style:ridge;
|
||||||
|
border-color: #603814;
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
.forum-category {
|
.forum-category {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
background-color: #657346;
|
background-color: #603814;
|
||||||
color: white;
|
color: white;
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
|
|
@ -112,16 +140,46 @@
|
||||||
content: "> ";
|
content: "> ";
|
||||||
margin-left: -1em;
|
margin-left: -1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
margin: 1em 0 2em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages li {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1em;
|
||||||
|
width: 100%;
|
||||||
|
line-height: 2.0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug {
|
||||||
|
background-color: #d0ebff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background-color: #b2f2bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
background-color: #ffec99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: #ffe3e3;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<h1 class="header">Christians of the Internet</h1>
|
<h1 class="header">Christians of the Internet</h1>
|
||||||
<div class="container nav">
|
<div class="container nav">
|
||||||
<center>
|
<center>
|
||||||
<a href="/">Home</a>
|
<a href="https://christian-webring.nekoweb.org/">Home</a>
|
||||||
<a href="widget.html">Widget</a>
|
<a href="https://christian-webring.nekoweb.org/widget.html">Widget</a>
|
||||||
<a href="/rules.html">Rules</a>
|
<a href="https://christian-webring.nekoweb.org/rules.html">Rules</a>
|
||||||
<a href="/member-list.html">Index</a>
|
<a href="https://christian-webring.nekoweb.org/member-list.html">Index</a>
|
||||||
<a href="/manager.html">Manager</a>
|
<a href="https://christian-webring.nekoweb.org/manager.html">Manager</a>
|
||||||
<a href="/form.html">Join</a>
|
<a href="{% url 'signup' %}">Join</a>
|
||||||
</center>
|
</center>
|
||||||
</div>
|
</div>
|
||||||
<div class="container nav" style="margin-top:-10px;">
|
<div class="container nav" style="margin-top:-10px;">
|
||||||
|
|
@ -141,7 +199,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<div class="container">
|
<div id="main" class="container">
|
||||||
|
{% if messages %}
|
||||||
|
<ul class="messages">
|
||||||
|
{% for message in messages %}
|
||||||
|
<li {% if message.tags %}class="{{ message.tags }}"{% endif %}>{{ message }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<h2>Welcome to the Forum Area</h2>
|
||||||
|
<p>
|
||||||
|
Here is where you can receive and view announcements, let us know feedback as it comes to mind, provide prayer requests for the prayer box, and enjoy conversation with other members if you'd like.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This exists as a way to garner a bit more community, should you desire such, as well as being relatively private to the members to prevent outsider abuse; or a place for those who are little less vocal about their faith in online spaces.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The conversation thread will be up as trial for 6 months. Please enjoy and aim to act in good faith; there are no rules besides the ones you read in order to join the ring.
|
||||||
|
</p>
|
||||||
<h2>Forum</h2>
|
<h2>Forum</h2>
|
||||||
{% for category, thread in threads.items %}
|
{% for category, thread in threads.items %}
|
||||||
<div class="forum-category">{{ category }}</div>
|
<div class="forum-category">{{ category }}</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>{{ user.username }}'s Profile</h2>
|
<h2 style="margin-bottom: 0px;">{{ user.username }}'s Profile</h2>
|
||||||
<img src="{{ user.avatar }}">
|
<a href="{% url 'password_change' %}">Change Password</a><br><br>
|
||||||
|
<img src="{{ user.avatar }}"><br>
|
||||||
<form method="post" enctype='multipart/form-data'>
|
<form method="post" enctype='multipart/form-data'>
|
||||||
<p>
|
<p>
|
||||||
<label for="id_avatar">Avatar:</label>
|
<label for="id_avatar">Avatar:</label>
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input type="submit" value="Log In" />
|
<input type="submit" value="Log In" />
|
||||||
</form>
|
</form>
|
||||||
|
<br>
|
||||||
|
<a href="{% url 'signup' %}">Sign Up!</a> | <a href="{% url 'password_reset' %}">Forgot Password?</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
input, textarea {
|
input, textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -11,19 +9,19 @@
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
<p>
|
<p>
|
||||||
{{ field.label_tag }}<br>
|
{{ field.label_tag }} {% if field.field.required == True %} <span style="color:red;">*</span> {% endif %}
|
||||||
|
<br>
|
||||||
{% if field.help_text %}
|
{% if field.help_text %}
|
||||||
<small style="color: grey">{{ field.help_text }}</small><br>
|
<small style="color: grey">{{ field.help_text|safe }}</small>
|
||||||
|
<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ field }}<br>
|
{{ field }}
|
||||||
{% for error in field.errors %}
|
<br>
|
||||||
<p style="color: red">{{ error }}</p>
|
{% for error in field.errors %}<p style="color: red">{{ error }}</p>{% endfor %}
|
||||||
{% endfor %}
|
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<button type="submit">Sign up</button>
|
<button type="submit">Sign up</button>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ post.created_by.avatar }}" alt="">
|
<img src="{{ post.created_by.avatar }}" alt="">
|
||||||
<small>
|
<small>
|
||||||
<b>{{ post.created_by }}</b> ({{ post.created_by.flair }})
|
<a href="{% url 'user' post.created_by.id %}"><b>{{ post.created_by }}</b></a> {% if post.created_by.flair %}({{ post.created_by.flair }}) {% endif %}
|
||||||
<br>
|
<br>
|
||||||
joined {{ post.created_by.date_joined|naturaltime }}
|
joined {{ post.created_by.date_joined|naturaltime }}
|
||||||
</small>
|
</small>
|
||||||
|
|
@ -81,6 +81,11 @@
|
||||||
<div class="markdownx">
|
<div class="markdownx">
|
||||||
<textarea name="content" cols="40" rows="10" required id="id_content" class="markdownx-editor" data-markdownx-editor-resizable="True" data-markdownx-urls-path="/markdownx/markdownify/" data-markdownx-upload-urls-path="/markdownx/upload/" data-markdownx-latency="500" style="max-height:150px; width:100%;">
|
<textarea name="content" cols="40" rows="10" required id="id_content" class="markdownx-editor" data-markdownx-editor-resizable="True" data-markdownx-urls-path="/markdownx/markdownify/" data-markdownx-upload-urls-path="/markdownx/upload/" data-markdownx-latency="500" style="max-height:150px; width:100%;">
|
||||||
</textarea>
|
</textarea>
|
||||||
|
{% if can_be_anon %}
|
||||||
|
<input type="checkbox" name="anonymous">
|
||||||
|
Submit Anonymously?
|
||||||
|
<br>
|
||||||
|
{% endif %}
|
||||||
<input type="submit" value="Submit" />
|
<input type="submit" value="Submit" />
|
||||||
<button type="button" onclick="toggleDisplay('markdownx-preview')">Show/Hide Preview</button>
|
<button type="button" onclick="toggleDisplay('markdownx-preview')">Show/Hide Preview</button>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
||||||
15
web/templates/user.html
Normal file
15
web/templates/user.html
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load humanize %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>{{ user.username }}'s Profile</h2>
|
||||||
|
<img src="{{ user.avatar }}" />
|
||||||
|
<br>
|
||||||
|
Joined: {{ user.date_joined|naturaltime }} {% if user.flair %}({{ user.flair }}) {% endif %}
|
||||||
|
<br>
|
||||||
|
<a href="{{ user.url }}" target="_blank">{{ user.url }}</a>
|
||||||
|
<p>
|
||||||
|
<b>Description:</b>
|
||||||
|
<br>
|
||||||
|
{{ user.description }}
|
||||||
|
{% endblock %}
|
||||||
173
web/views.py
173
web/views.py
|
|
@ -1,72 +1,37 @@
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
|
||||||
from django.contrib.auth import login, logout
|
|
||||||
|
|
||||||
|
from django.contrib.auth import login, logout
|
||||||
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from markdownx.utils import markdownify
|
||||||
|
|
||||||
from web.forms import SignupForm, ThreadPostForm, EditProfileForm
|
from web.forms import SignupForm, ThreadPostForm, EditProfileForm
|
||||||
from web.models.forum_subcategory import ForumSubcategory
|
from web.models.forum_subcategory import ForumSubcategory
|
||||||
from web.models.forum_post import ForumPost
|
from web.models.forum_post import ForumPost
|
||||||
|
from web.models.custom_user import CustomUser
|
||||||
from markdownx.utils import markdownify
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
# def send_email(subject, body, recipients=None):
|
||||||
return render(request, 'index.html')
|
# if not recipients:
|
||||||
|
# recipients = [x[0] for x in CustomUser.objects.filter(is_superuser=True).all().values_list('email') if x[0] != '']
|
||||||
|
# send_mail(subject, body, 'domdit@gmail.com', recipients, fail_silently=False)
|
||||||
|
|
||||||
|
|
||||||
# FORUM ###################################################
|
def is_member(user):
|
||||||
def forum_threads(request):
|
if user.is_authenticated:
|
||||||
parsed_forum_threads = {}
|
return user.groups.filter(name='Member').exists()
|
||||||
forum_subcategories = ForumSubcategory.objects.filter(active=True).order_by('-created_at').order_by('sticky').all()
|
return False
|
||||||
|
|
||||||
for forum_subcategory in forum_subcategories:
|
|
||||||
if forum_subcategory.forum_category.title not in parsed_forum_threads:
|
|
||||||
parsed_forum_threads[forum_subcategory.forum_category.title] = []
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'id': forum_subcategory.id,
|
|
||||||
'title': forum_subcategory.title,
|
|
||||||
'description': forum_subcategory.description,
|
|
||||||
'post_count': len(forum_subcategory.posts.all()),
|
|
||||||
'most_recent_poster': forum_subcategory.posts.first().created_by if forum_subcategory.posts.first() else 'admin',
|
|
||||||
'most_recent_post_date': forum_subcategory.posts.first().created_at if forum_subcategory.posts.first() else forum_subcategory.created_at,
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed_forum_threads[forum_subcategory.forum_category.title].append(data)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'threads': parsed_forum_threads
|
|
||||||
}
|
|
||||||
return render(request, 'forum_threads.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
def thread(request, thread_id):
|
# def test_endpoint(request):
|
||||||
thread = get_object_or_404(ForumSubcategory, pk=thread_id)
|
# send_email('this is a subject', 'this is a body')
|
||||||
|
# return render(request, 'index.html')
|
||||||
if request.method == 'POST':
|
|
||||||
form = ThreadPostForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
post = ForumPost(
|
|
||||||
content=markdownify(form.data['content']),
|
|
||||||
forum_subcategory=thread,
|
|
||||||
created_by=request.user,
|
|
||||||
edited=False,
|
|
||||||
sticky=False,
|
|
||||||
)
|
|
||||||
post.save()
|
|
||||||
|
|
||||||
form = ThreadPostForm()
|
|
||||||
posts = ForumPost.objects.filter(forum_subcategory=thread).all()
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'thread_name': thread.title,
|
|
||||||
'thread_category': thread.forum_category.title,
|
|
||||||
'posts': posts,
|
|
||||||
'form': form
|
|
||||||
}
|
|
||||||
return render(request, 'thread.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
# ACCOUNT MANAGEMENT ######################################
|
# ACCOUNT MANAGEMENT ######################################
|
||||||
|
|
@ -76,7 +41,9 @@ def signup(request):
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
user = form.save()
|
user = form.save()
|
||||||
login(request, user)
|
login(request, user)
|
||||||
return redirect('index')
|
messages.success(request, "You have successfully applied for membership, we will review your submission and send you an email once we have added you as a member! In the meantime, please start setting up the webring widget on your website!")
|
||||||
|
|
||||||
|
return redirect(reverse('login'))
|
||||||
else:
|
else:
|
||||||
form = SignupForm()
|
form = SignupForm()
|
||||||
|
|
||||||
|
|
@ -86,11 +53,20 @@ def signup(request):
|
||||||
return render(request, 'signup.html', context)
|
return render(request, 'signup.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
def denied(request):
|
||||||
|
messages.warning(request, "You are not a member yet and cannot access the member site. Please wait for an admin to add you. If you have waited a long time, please send an email to domdit@gmail.com")
|
||||||
|
return redirect(reverse('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||||
def custom_logout(request):
|
def custom_logout(request):
|
||||||
logout(request)
|
logout(request)
|
||||||
return redirect(reverse('login'))
|
return redirect(reverse('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||||
def profile(request):
|
def profile(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = EditProfileForm(request.POST)
|
form = EditProfileForm(request.POST)
|
||||||
|
|
@ -117,3 +93,88 @@ def profile(request):
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
return render(request, 'profile.html', context)
|
return render(request, 'profile.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||||
|
def user_profile(request, user_id):
|
||||||
|
context = {
|
||||||
|
'user': get_object_or_404(CustomUser, pk=user_id),
|
||||||
|
}
|
||||||
|
return render(request, 'user.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
# FORUM ###################################################
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||||
|
def forum_threads(request):
|
||||||
|
parsed_forum_threads = {}
|
||||||
|
forum_subcategories = ForumSubcategory.objects.filter(active=True).order_by('created_at').order_by('-sticky').all()
|
||||||
|
|
||||||
|
for forum_subcategory in forum_subcategories:
|
||||||
|
if forum_subcategory.forum_category.title not in parsed_forum_threads:
|
||||||
|
parsed_forum_threads[forum_subcategory.forum_category.title] = []
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'id': forum_subcategory.id,
|
||||||
|
'title': forum_subcategory.title,
|
||||||
|
'description': forum_subcategory.description,
|
||||||
|
'post_count': len(forum_subcategory.posts.all()),
|
||||||
|
'most_recent_poster': forum_subcategory.posts.first().created_by if forum_subcategory.posts.first() else 'admin',
|
||||||
|
'most_recent_post_date': forum_subcategory.posts.first().created_at if forum_subcategory.posts.first() else forum_subcategory.created_at,
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_forum_threads[forum_subcategory.forum_category.title].append(data)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'threads': parsed_forum_threads
|
||||||
|
}
|
||||||
|
return render(request, 'forum_threads.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||||
|
def thread(request, thread_id):
|
||||||
|
thread = get_object_or_404(ForumSubcategory, pk=thread_id)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
anonymous = False
|
||||||
|
form = ThreadPostForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
if 'anonymous' in form.data:
|
||||||
|
if form.data['anonymous'] == 'on':
|
||||||
|
anonymous = True
|
||||||
|
post = ForumPost(
|
||||||
|
content=markdownify(form.data['content']),
|
||||||
|
forum_subcategory=thread,
|
||||||
|
created_by=CustomUser.objects.filter(username='Anonymous').first() if anonymous else request.user,
|
||||||
|
edited=False,
|
||||||
|
sticky=False,
|
||||||
|
)
|
||||||
|
post.save()
|
||||||
|
|
||||||
|
form = ThreadPostForm()
|
||||||
|
posts = ForumPost.objects.filter(forum_subcategory=thread).all()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'can_be_anon': thread.can_by_anon,
|
||||||
|
'thread_name': thread.title,
|
||||||
|
'thread_category': thread.forum_category.title,
|
||||||
|
'posts': posts,
|
||||||
|
'form': form
|
||||||
|
}
|
||||||
|
return render(request, 'thread.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
# API ##############################################
|
||||||
|
def users(request):
|
||||||
|
parsed_users = {'users': []}
|
||||||
|
admin_only = request.GET.get('admin', False)
|
||||||
|
users = CustomUser.objects.filter(is_active=True).filter(groups__name='Member')
|
||||||
|
if admin_only == 'true':
|
||||||
|
users = users.filter(is_superuser=True)
|
||||||
|
|
||||||
|
users = users.all().values('username', 'description', 'url')
|
||||||
|
for user in users:
|
||||||
|
parsed_users['users'].append(user)
|
||||||
|
return JsonResponse(parsed_users)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue