Added forums
This commit is contained in:
parent
cf20df8d33
commit
912a5dcb25
@ -48,6 +48,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'django_forms_bootstrap',
|
||||||
'expert_witnesses',
|
'expert_witnesses',
|
||||||
'forum.apps.ForumConfig',
|
'forum.apps.ForumConfig',
|
||||||
'main.apps.MainConfig',
|
'main.apps.MainConfig',
|
||||||
@ -94,12 +95,14 @@ WSGI_APPLICATION = 'OACPL.wsgi.application'
|
|||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': 'postgres',
|
||||||
|
'USER': 'postgres',
|
||||||
|
'HOST': 'db',
|
||||||
|
'PORT': 5432,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@ urlpatterns = [
|
|||||||
url(r'^contact/', main.views.contact, name='contact'),
|
url(r'^contact/', main.views.contact, name='contact'),
|
||||||
url(r'^experts/(?P<id>\d+)', expert_witnesses.views.viewer, name='expert'),
|
url(r'^experts/(?P<id>\d+)', expert_witnesses.views.viewer, name='expert'),
|
||||||
url(r'^experts/', expert_witnesses.views.browser, name='experts'),
|
url(r'^experts/', expert_witnesses.views.browser, name='experts'),
|
||||||
url(r'^forum/post/(?P<post>\d*)', forum.views.post, name='post'),
|
url(r'^forum/post/(?P<post>\d*)', forum.views.viewPost, name='post'),
|
||||||
url(r'^forum/comment', forum.views.comment, name='comment'),
|
|
||||||
url(r'^forum/(?P<thread>\d*)?', forum.views.view, name='forum'),
|
url(r'^forum/(?P<thread>\d*)?', forum.views.view, name='forum'),
|
||||||
url(r'^login/', main.views.login, name='login'),
|
url(r'^login/', main.views.login, name='login'),
|
||||||
url(r'^logout/', main.views.logout, name='logout'),
|
url(r'^logout/', main.views.logout, name='logout'),
|
||||||
|
@ -8,15 +8,15 @@ admin.site.register(Thread)
|
|||||||
|
|
||||||
@admin.register(Post)
|
@admin.register(Post)
|
||||||
class PostAdmin(admin.ModelAdmin):
|
class PostAdmin(admin.ModelAdmin):
|
||||||
list_display = ['title', 'topic', 'creator', 'created']
|
list_display = ['title', 'resolved', 'topic', 'creator', 'created']
|
||||||
search_fields = ['title', 'topic', 'creator', 'created']
|
search_fields = ['title', 'resolved', 'topic', 'creator', 'created']
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
if obj:
|
if obj:
|
||||||
self.fields = ['creator', 'created', 'topic', 'title', 'question']
|
self.fields = ['creator', 'created', 'resolved', 'title', 'topic', 'question']
|
||||||
self.readonly_fields = ['creator', 'created']
|
self.readonly_fields = ['creator', 'created']
|
||||||
else:
|
else:
|
||||||
self.fields = ['topic', 'title', 'question']
|
self.fields = ['title', 'topic', 'question']
|
||||||
self.readonly_fields = []
|
self.readonly_fields = []
|
||||||
return super(PostAdmin, self).get_form(request, obj, **kwargs)
|
return super(PostAdmin, self).get_form(request, obj, **kwargs)
|
||||||
|
|
||||||
|
29
forum/forms.py
Normal file
29
forum/forms.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from tinymce import TinyMCE
|
||||||
|
|
||||||
|
from .models import Comment, Post, Thread
|
||||||
|
|
||||||
|
|
||||||
|
class CommentForm(forms.ModelForm):
|
||||||
|
reply = forms.CharField(widget=TinyMCE(mce_attrs={'height': 200}))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Comment
|
||||||
|
fields = ['reply']
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePostForm(forms.ModelForm):
|
||||||
|
question = forms.CharField(widget=TinyMCE(mce_attrs={'height': 200}))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = ['title', 'topic', 'question']
|
||||||
|
|
||||||
|
|
||||||
|
class EditPostForm(forms.ModelForm):
|
||||||
|
question = forms.CharField(widget=TinyMCE(mce_attrs={'height': 200}))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = ['question']
|
@ -6,45 +6,126 @@
|
|||||||
<div class="container py-3">
|
<div class="container py-3">
|
||||||
<div class="col-12 bg-white p-3">
|
<div class="col-12 bg-white p-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
{% if post.creator == user and not post.resolved or perms.forum.change_post and not post.resolved %}
|
||||||
|
<div class="btn-group float-right">
|
||||||
|
<button class="btn" data-toggle="modal" data-target="#editModal"><i class="fa fa-pencil"></i></button>
|
||||||
|
{% if post.creator == user or perms.forum.delete_post %}
|
||||||
|
<button class="btn" data-toggle="modal" data-target="#deleteModal"><i class="fa fa-trash"></i></button>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-danger" data-toggle="modal" data-target="#closeModal">Close</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<h2 class="mb-0">{{ post.title }}</h2>
|
<h2 class="mb-0">{{ post.title }}</h2>
|
||||||
<small class="text-muted">Asked By: {{ post.creator }}</small>
|
<span class="text-muted">
|
||||||
|
{% if post.resolved %}<span class="badge badge-sm badge-danger">Closed</span> {% endif %}
|
||||||
|
Asked By: {{ post.creator }}, {{ post.created | date }}
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 mt-3">
|
<div class="col-12 my-3">
|
||||||
{{ post }}
|
{{ post.question | safe }}
|
||||||
</div>
|
</div>
|
||||||
{% for comment in comments %}
|
{% if comments %}
|
||||||
<div class="col-12 bg-white p-3 mt-3 rounded" style="border: 1px solid rgba(0, 0, 0, 0.2">
|
<div class="col-12 mt-5">
|
||||||
<div class="col-12">
|
<h4>Answers</h4>
|
||||||
<small class="text-muted">Answered By: {{ comment.creator }}</small>
|
<ul class="list-group w-100">
|
||||||
<div class="w-100">
|
{% for comment in comments %}
|
||||||
{% autoescape off %}
|
<li class="list-group-item">
|
||||||
{{ comment }}
|
<span class="text-muted">{{ comment.creator }}, {{ comment.created | date }}</span>
|
||||||
{% endautoescape %}
|
{% autoescape off %}
|
||||||
</div>
|
{{ comment }}
|
||||||
</div>
|
{% endautoescape %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 bg-white mt-3 py-3" style="position: relative">
|
{% if not post.resolved and perms.forum.add_comment %}
|
||||||
<div>
|
<div class="col-12 bg-white mt-3 py-3" style="overflow: auto">
|
||||||
<h5>Comment</h5>
|
<div class="col-12">
|
||||||
<form method="post" action="{% url 'comment' %}">
|
<h5>Comment</h5>
|
||||||
{% csrf_token %}
|
<form method="post">
|
||||||
<input name="post" type="hidden" value="{{ post.id }}">
|
<div>
|
||||||
<textarea class="form-control" name="comment" value="{{ comment }}"
|
{% csrf_token %}
|
||||||
{% if not request.user.is_authenticated or not perms.form.add_comment %}disabled{% endif %}></textarea>
|
<input type="hidden" name="request" value="comment">
|
||||||
<button class="btn btn-primary float-right">Create</button>
|
{{ form.reply }}
|
||||||
</form>
|
<button class="btn btn-primary float-right mt-3">Create</button>
|
||||||
{% if not request.user.is_authenticated %}
|
</div>
|
||||||
<span style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">You must <a
|
</form>
|
||||||
href="{% url 'login' %}">login</a> to comment</span>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% if request.user.is_authenticatednot and perms.form.add_comment %}
|
|
||||||
<span style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">You do not have permission to comment</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModal" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Edit</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="editForm" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="request" value="edit">
|
||||||
|
{{ editForm.question }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="submit" class="btn btn-primary" form="editForm">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModal" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Delete Post</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you would like to delete this post? It cannot be undone.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="request" value="delete">
|
||||||
|
<button class="btn"><i class="fa fa-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="closeModal" tabindex="-1" role="dialog" aria-labelledby="closeModal" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Close Post</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you would like to close this issue? No one will be able to respond anylonger.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="request" value="resolve">
|
||||||
|
<button class="btn btn-danger" data-toggle="modal" data-target="#closeModal">Close</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load bootstrap_tags %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
|
{% if form.errors %}
|
||||||
|
$('#create-post-modal').modal(show = true);
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
$('#my-posts').popover({
|
$('#my-posts').popover({
|
||||||
title: 'My Posts',
|
title: 'My Posts',
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
html: true,
|
html: true,
|
||||||
content: '<ul class="list-group">{% if myPosts.count == 0 %}None{% endif %}{% for post in myPosts %}<a href="{% url 'post' post.id %}" class="list-group-item list-group-item-action">{{ post.title }}</a>{% endfor %}</ul>'
|
content: '<ul class="list-group">{% if myPosts.count == 0 %}None{% endif %}{% for post in myPosts %}<a href="{% url 'post' post.id %}" class="list-group-item list-group-item-action">{{ post.title }}</a>{% endfor %}</ul>'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#createPostButton').click(function () {
|
||||||
|
$('#createPost').submit();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -42,17 +51,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
{% if posts %}
|
||||||
<div class="col-12">
|
<div class="row">
|
||||||
<h3 class="text-white d-inline">Posts</h3>
|
<div class="col-12">
|
||||||
<ul class="list-group">
|
<h3 class="text-white d-inline">{% if thread %}"{{ thread }}" {% endif %}Posts</h3>
|
||||||
{% for post in posts %}
|
<ul class="list-group">
|
||||||
<a href="{% url 'post' post.id %}"
|
{% for post in posts %}
|
||||||
class="list-group-item list-group-item-action">{{ post.title }}</a>
|
<a href="{% url 'post' post.id %}" class="list-group-item list-group-item-action">
|
||||||
{% endfor %}
|
{% if post.resolved %}<span class="badge badge-danger">Closed</span> {% endif %}
|
||||||
</ul>
|
{{ post.title }}
|
||||||
|
<span class="float-right text-muted">{{ post.creator }}, {{ post.created | date }}</span>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -66,29 +80,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="col-12">
|
<form id="createPost" method="post">
|
||||||
<form>
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
{{ form | as_bootstrap }}
|
||||||
<label for="title">Title</label>
|
</form>
|
||||||
<input class="form-control" name="title">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="thread">Topic</label>
|
|
||||||
<select class="form-control" name="thread">
|
|
||||||
{% for thread in threads %}
|
|
||||||
<option value="{{ thread.id }}">{{ thread.topic }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="title">Body</label>
|
|
||||||
<textarea class="form-control" name="body"></textarea>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary">Create</button>
|
<button id="createPostButton" type="button" class="btn btn-primary">Create</button>
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,40 +1,56 @@
|
|||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
|
from .forms import CommentForm, CreatePostForm, EditPostForm
|
||||||
from .models import Thread, Post, Comment
|
from .models import Thread, Post, Comment
|
||||||
|
|
||||||
|
|
||||||
def view(request, thread=None):
|
def view(request, thread=None):
|
||||||
myPosts = None
|
if request.method == 'POST':
|
||||||
|
form = CreatePostForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.creator = request.user
|
||||||
|
instance.save()
|
||||||
|
return redirect('post', post=instance.id)
|
||||||
|
else:
|
||||||
|
form = CreatePostForm()
|
||||||
|
|
||||||
|
my_posts = None
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
myPosts = Post.objects.filter(creator=request.user)
|
my_posts = Post.objects.filter(creator=request.user, resolved=False)
|
||||||
if not thread:
|
if not thread:
|
||||||
threads = Thread.objects.all()
|
threads = Thread.objects.all()
|
||||||
posts = Post.objects.order_by('created')[:10]
|
thread_name = None
|
||||||
|
posts = Post.objects.filter(resolved=False).order_by('-created')[:10]
|
||||||
else:
|
else:
|
||||||
threads = None
|
threads = None
|
||||||
posts = Post.objects.filter(topic=thread)
|
thread_name = Thread.objects.get(id=thread).topic
|
||||||
return render(request, 'view.html', {'threads': threads, 'posts': posts, 'myPosts': myPosts})
|
posts = Post.objects.filter(topic=thread).order_by('-created')
|
||||||
|
return render(request, 'view.html', {'threads': threads, 'posts': posts, 'myPosts': my_posts, 'thread': thread_name, 'form': form})
|
||||||
|
|
||||||
|
|
||||||
def post(request, post):
|
def viewPost(request, post):
|
||||||
this_post = Post.objects.get(id=post)
|
this_post = Post.objects.get(id=post)
|
||||||
comments = Comment.objects.filter(post=post)
|
|
||||||
return render(request, 'post.html', {'post': this_post, 'comments': comments})
|
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if request.POST.get('request') == 'comment':
|
||||||
|
form = CommentForm(request.POST)
|
||||||
|
Comment.objects.create(post_id=post,
|
||||||
|
reply=form.data.get('reply'),
|
||||||
|
creator=request.user)
|
||||||
|
elif request.POST.get('request') == 'edit':
|
||||||
|
form = EditPostForm(request.POST, instance=this_post)
|
||||||
|
form.save()
|
||||||
|
elif request.POST.get('request') == 'resolve':
|
||||||
|
if this_post.creator == request.user or request.user.has_perm('forum.change_post'):
|
||||||
|
this_post.resolved = True
|
||||||
|
this_post.save()
|
||||||
|
elif request.POST.get('request') == 'delete':
|
||||||
|
if this_post.creator == request.user or request.user.has_perm('forum.delete_post'):
|
||||||
|
this_post.delete()
|
||||||
|
return redirect('forum')
|
||||||
|
|
||||||
def create(request):
|
form = CommentForm()
|
||||||
pass
|
edit_form = EditPostForm(instance=this_post)
|
||||||
|
comments = Comment.objects.filter(post=this_post)
|
||||||
|
return render(request, 'post.html', {'post': this_post, 'comments': comments, 'form': form, 'editForm': edit_form})
|
||||||
def comment(request):
|
|
||||||
success = False
|
|
||||||
post = Post.objects.get(id=request.POST.get('post'))
|
|
||||||
comments = Comment.objects.filter(post=post)
|
|
||||||
try:
|
|
||||||
|
|
||||||
success = Comment.objects.create(post=post,
|
|
||||||
reply=request.POST.get('comment'),
|
|
||||||
creator=request.user)
|
|
||||||
success.save()
|
|
||||||
finally:
|
|
||||||
return redirect('forum', post.id)
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
bootstrap-admin==0.3.7.1
|
bootstrap-admin==0.3.7.1
|
||||||
Django==1.11.5
|
Django==1.11.5
|
||||||
|
django-forms-bootstrap==3.1.0
|
||||||
|
django-tinymce4-lite==1.7.0
|
||||||
Pillow==4.2.1
|
Pillow==4.2.1
|
||||||
requests==2.11.1
|
requests==2.11.1
|
||||||
django-tinymce4-lite==1.7.0
|
|
||||||
|
Loading…
Reference in New Issue
Block a user