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] logout
|
||||
[ ] change password
|
||||
[x] member profile update and avatar
|
||||
[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] 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
|
||||
[ ] users must be authed
|
||||
[ ] signup must redirect NOT auto grant users access, and MUST give them some info on what to expect
|
||||
[ ] page that displays all users and links to their websites (this already exists on neko, but better to automate it)
|
||||
[ ] Pick better anonymous avatar
|
||||
[ ] determine where which pages should live where
|
||||
[ ] 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_ROOT = 'static/'
|
||||
|
||||
site_root = '/home/dominic/repos/cwr'
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(site_root, 'assets/'),
|
||||
)
|
||||
LOGIN_REDIRECT_URL = 'forum_threads'
|
||||
LOGOUT_REDIRECT_URL = "login"
|
||||
# Default primary key field type
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
from django.contrib import admin
|
||||
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 = [
|
||||
path('markdownx/', include('markdownx.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/users/', users, name='users'),
|
||||
path("accounts/", include("django.contrib.auth.urls")),
|
||||
path("accounts/signup/", signup, name="signup"),
|
||||
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("", forum_threads, name='index'),
|
||||
path("forum/threads/", forum_threads, name='forum_threads'),
|
||||
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):
|
||||
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 = ''
|
||||
|
||||
url = forms.URLField(max_length=200, help_text='Required')
|
||||
description = forms.CharField(widget=forms.Textarea)
|
||||
rule_conf = forms.BooleanField(label='Have you read the rules?')
|
||||
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, 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?', 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'})
|
||||
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:
|
||||
model = CustomUser
|
||||
|
|
@ -27,6 +35,7 @@ class SignupForm(UserCreationForm):
|
|||
'url',
|
||||
'description',
|
||||
'rule_conf',
|
||||
'email_only',
|
||||
'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='')
|
||||
avatar = models.TextField(default='', blank=True)
|
||||
rule_conf = models.BooleanField(default=True)
|
||||
comments = models.TextField(default='')
|
||||
flair = models.CharField(default='')
|
||||
comments = models.TextField(default='', blank=True)
|
||||
flair = models.CharField(default='', blank=True)
|
||||
email_only = models.BooleanField(default=False)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class ForumSubcategory(BaseModel):
|
|||
description = models.TextField()
|
||||
forum_category = models.ForeignKey(ForumCategory, on_delete=models.CASCADE)
|
||||
sticky = models.BooleanField()
|
||||
can_by_anon = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
db_table = 'forum_subcategory'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script async src="https://tally.so/widgets/embed.js"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Christians of the Internet</title>
|
||||
|
|
@ -9,124 +9,182 @@
|
|||
</head>
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
background-color: #d7c5e0;
|
||||
background-image: url("https://sadhost.neocities.org/images/tiles/bgif016.gif");
|
||||
background-repeat: repeat;
|
||||
background-attachment: fixed;
|
||||
color: #000000;
|
||||
font-family: "Cormorant Garamond", serif;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.1em;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
background-color: #868F69;
|
||||
background-repeat: repeat;
|
||||
background-attachment: fixed;
|
||||
color: #000000;
|
||||
font-family: "Cormorant Garamond", serif;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.1em;
|
||||
background-image: url("{% static 'img/bg.png' %}");
|
||||
}
|
||||
|
||||
h1.header {
|
||||
font-family: "Special Elite", serif;
|
||||
text-align: center;
|
||||
font-size: 45px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
h1.header {
|
||||
text-align: center;
|
||||
margin: 30px 0;
|
||||
color: #603814;
|
||||
font-family: "Girassol", serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 70px;
|
||||
text-shadow: 1px 1px 0px #868F69, 2px 2px 0px #254117;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #ffffff;
|
||||
width: 70%;
|
||||
max-width: 1400px;
|
||||
padding: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
.container {
|
||||
background-color: #ffffff;
|
||||
width: 70%;
|
||||
max-width: 1400px;
|
||||
padding: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.container {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.container {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding: 1px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.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-left: 20px;
|
||||
padding-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nav a {
|
||||
text-decoration: none;
|
||||
margin-right: 5px;
|
||||
color: #657346;
|
||||
}
|
||||
.nav a {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav a:hover {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nav a:hover {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav a:active {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nav a:active {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.forum-category {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #657346;
|
||||
color: white;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 3px;
|
||||
padding-left: 10px;
|
||||
box-sizing: border-box;
|
||||
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-thread {
|
||||
width: 100%;
|
||||
border: solid #657346 1px;
|
||||
border-top: 0px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.forum-category {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #603814;
|
||||
color: white;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 3px;
|
||||
padding-left: 10px;
|
||||
box-sizing: border-box;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table {
|
||||
line-height: 1em;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.forum-thread {
|
||||
width: 100%;
|
||||
border: solid #657346 1px;
|
||||
border-top: 0px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #657346;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
table {
|
||||
line-height: 1em;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #657346;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: green;
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
position: absolute;
|
||||
content: "> ";
|
||||
margin-left: -1em;
|
||||
}
|
||||
blockquote {
|
||||
color: green;
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
position: absolute;
|
||||
content: "> ";
|
||||
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>
|
||||
<h1 class="header">Christians of the Internet</h1>
|
||||
<div class="container nav">
|
||||
<center>
|
||||
<a href="/">Home</a>
|
||||
<a href="widget.html">Widget</a>
|
||||
<a href="/rules.html">Rules</a>
|
||||
<a href="/member-list.html">Index</a>
|
||||
<a href="/manager.html">Manager</a>
|
||||
<a href="/form.html">Join</a>
|
||||
<a href="https://christian-webring.nekoweb.org/">Home</a>
|
||||
<a href="https://christian-webring.nekoweb.org/widget.html">Widget</a>
|
||||
<a href="https://christian-webring.nekoweb.org/rules.html">Rules</a>
|
||||
<a href="https://christian-webring.nekoweb.org/member-list.html">Index</a>
|
||||
<a href="https://christian-webring.nekoweb.org/manager.html">Manager</a>
|
||||
<a href="{% url 'signup' %}">Join</a>
|
||||
</center>
|
||||
</div>
|
||||
<div class="container nav" style="margin-top:-10px;">
|
||||
<center>
|
||||
<a href="{% url 'forum_threads' %}">Forum</a>
|
||||
<a href="{% url 'forum_threads' %}">Forum</a>
|
||||
<a href="{% url 'profile' %}">Profile</a>
|
||||
<a href="{% url 'custom_logout' %}">Logout</a>
|
||||
</center>
|
||||
|
|
@ -141,7 +199,14 @@
|
|||
}
|
||||
}
|
||||
</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 %}
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
{% extends "base.html" %}
|
||||
{% 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>
|
||||
{% for category, thread in threads.items %}
|
||||
<div class="forum-category">{{ category }}</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>{{ user.username }}'s Profile</h2>
|
||||
<img src="{{ user.avatar }}">
|
||||
<h2 style="margin-bottom: 0px;">{{ user.username }}'s Profile</h2>
|
||||
<a href="{% url 'password_change' %}">Change Password</a><br><br>
|
||||
<img src="{{ user.avatar }}"><br>
|
||||
<form method="post" enctype='multipart/form-data'>
|
||||
<p>
|
||||
<label for="id_avatar">Avatar:</label>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>Login</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Log In" />
|
||||
</form>
|
||||
<h2>Login</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Log In" />
|
||||
</form>
|
||||
<br>
|
||||
<a href="{% url 'signup' %}">Sign Up!</a> | <a href="{% url 'password_reset' %}">Forgot Password?</a>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
<style>
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -10,22 +8,22 @@
|
|||
.checkmark {
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<p>
|
||||
{{ field.label_tag }}<br>
|
||||
{% if field.help_text %}
|
||||
<small style="color: grey">{{ field.help_text }}</small><br>
|
||||
{% endif %}
|
||||
{{ field }}<br>
|
||||
{% for error in field.errors %}
|
||||
<p style="color: red">{{ error }}</p>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
</style>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<p>
|
||||
{{ field.label_tag }} {% if field.field.required == True %} <span style="color:red;">*</span> {% endif %}
|
||||
<br>
|
||||
{% if field.help_text %}
|
||||
<small style="color: grey">{{ field.help_text|safe }}</small>
|
||||
<br>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
<br>
|
||||
{% for error in field.errors %}<p style="color: red">{{ error }}</p>{% endfor %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
{% for post in posts %}
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{{post.created_by.avatar}}" alt="">
|
||||
<img src="{{ post.created_by.avatar }}" alt="">
|
||||
<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>
|
||||
joined {{ post.created_by.date_joined|naturaltime }}
|
||||
</small>
|
||||
|
|
@ -81,18 +81,23 @@
|
|||
<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>
|
||||
<input type="submit" value="Submit" />
|
||||
<button type="button" onclick="toggleDisplay('markdownx-preview')">Show/Hide Preview</button>
|
||||
<br>
|
||||
<br>
|
||||
<div id="markdownx-preview"
|
||||
class="markdownx-preview"
|
||||
style="display:none;
|
||||
width:100%;
|
||||
font-size: 12px;
|
||||
margin-bottom:5px;
|
||||
padding-top:0px"></div>
|
||||
</div>
|
||||
</form>
|
||||
<a href="{% url 'forum_threads' %}"> << Go Back</a>
|
||||
{% endblock %}
|
||||
{% if can_be_anon %}
|
||||
<input type="checkbox" name="anonymous">
|
||||
Submit Anonymously?
|
||||
<br>
|
||||
{% endif %}
|
||||
<input type="submit" value="Submit" />
|
||||
<button type="button" onclick="toggleDisplay('markdownx-preview')">Show/Hide Preview</button>
|
||||
<br>
|
||||
<br>
|
||||
<div id="markdownx-preview"
|
||||
class="markdownx-preview"
|
||||
style="display:none;
|
||||
width:100%;
|
||||
font-size: 12px;
|
||||
margin-bottom:5px;
|
||||
padding-top:0px"></div>
|
||||
</div>
|
||||
</form>
|
||||
<a href="{% url 'forum_threads' %}"> << Go Back</a>
|
||||
{% endblock %}
|
||||
|
|
|
|||
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 json
|
||||
from io import BytesIO
|
||||
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 markdownx.utils import markdownify
|
||||
|
||||
from web.forms import SignupForm, ThreadPostForm, EditProfileForm
|
||||
from web.models.forum_subcategory import ForumSubcategory
|
||||
from web.models.forum_post import ForumPost
|
||||
|
||||
from markdownx.utils import markdownify
|
||||
from web.models.custom_user import CustomUser
|
||||
|
||||
|
||||
def index(request):
|
||||
return render(request, 'index.html')
|
||||
# def send_email(subject, body, recipients=None):
|
||||
# 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 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)
|
||||
def is_member(user):
|
||||
if user.is_authenticated:
|
||||
return user.groups.filter(name='Member').exists()
|
||||
return False
|
||||
|
||||
|
||||
def thread(request, thread_id):
|
||||
thread = get_object_or_404(ForumSubcategory, pk=thread_id)
|
||||
|
||||
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)
|
||||
# def test_endpoint(request):
|
||||
# send_email('this is a subject', 'this is a body')
|
||||
# return render(request, 'index.html')
|
||||
|
||||
|
||||
# ACCOUNT MANAGEMENT ######################################
|
||||
|
|
@ -76,7 +41,9 @@ def signup(request):
|
|||
if form.is_valid():
|
||||
user = form.save()
|
||||
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:
|
||||
form = SignupForm()
|
||||
|
||||
|
|
@ -86,11 +53,20 @@ def signup(request):
|
|||
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):
|
||||
logout(request)
|
||||
return redirect(reverse('login'))
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(is_member, login_url='/accounts/denied/')
|
||||
def profile(request):
|
||||
if request.method == 'POST':
|
||||
form = EditProfileForm(request.POST)
|
||||
|
|
@ -117,3 +93,88 @@ def profile(request):
|
|||
'form': form,
|
||||
}
|
||||
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