Finishing up on comment functionality

comments
Sander Ferdinand 6 years ago
parent 863e91e3f2
commit 40b95ad57d

@ -103,6 +103,7 @@ class Proposal(base):
user = relationship("User", back_populates="proposals")
payouts = relationship("Payout", back_populates="proposal")
comments = relationship("Comment", back_populates="proposal", lazy='select')
def __init__(self, headline, content, category, user: User):
if not headline or not content:
@ -114,8 +115,6 @@ class Proposal(base):
raise Exception('wrong category')
self.category = category
@property
def json(self):
return {
@ -133,14 +132,30 @@ class Proposal(base):
@classmethod
def find_by_id(cls, pid: int):
from wowfunding.factory import db_session
q = cls.query
q = q.filter(Proposal.id == pid)
result = q.first()
if not result:
return
# check if we have a valid addr_donation generated. if not, make one.
if not result.addr_donation:
Proposal.generate_donation_addr(result)
q = db_session.query(Comment)
q = q.filter(Comment.proposal_id == result.id)
q = q.filter(Comment.replied_to == None)
comments = q.all()
for c in comments:
q = db_session.query(Comment)
q = q.filter(Comment.proposal_id == result.id)
q = q.filter(Comment.replied_to == c.id)
_c = q.all()
setattr(c, 'comments', _c)
setattr(result, '_comments', comments)
return result
@property
@ -274,26 +289,34 @@ class Comment(base):
__tablename__ = "comments"
id = sa.Column(sa.Integer, primary_key=True)
proposal_id = sa.Column(sa.Integer, sa.ForeignKey('proposals.id'))
proposal = relationship("Proposal", back_populates="comments")
user_id = sa.Column(sa.Integer, sa.ForeignKey('users.user_id'), nullable=False)
user = relationship("User", back_populates="comments")
date_added = sa.Column(sa.TIMESTAMP, default=datetime.now)
message = sa.Column(sa.VARCHAR, nullable=False)
replied_to = sa.Column(sa.ForeignKey("comments.id"))
locked = sa.Column(sa.Boolean, default=False)
ix_comment_replied_to = sa.Index("ix_comment_replied_to", replied_to)
ix_comment_proposal_id = sa.Index("ix_comment_proposal_id", proposal_id)
@staticmethod
def get(comment_id: int):
def find_by_id(cid: int):
from wowfunding.factory import db_session
return db_session.query(Comment).filter(Comment.id == comment_id).first()
return db_session.query(Comment).filter(Comment.id == cid).first()
@staticmethod
def remove(comment_id: int):
def remove(cid: int):
from wowfunding.factory import db_session
from flask.ext.login import current_user
if current_user.id != user_id and not current_user.admin:
raise Exception("no rights to remove this comment")
comment = Comment.get(comment_id=comment_id)
comment = Comment.get(cid=cid)
try:
comment.delete()
db_session.commit()
@ -303,12 +326,12 @@ class Comment(base):
raise
@staticmethod
def lock(comment_id: int):
def lock(cid: int):
from wowfunding.factory import db_session
from flask.ext.login import current_user
if not current_user.admin:
raise Exception("admin required")
comment = Comment.get(comment_id=comment_id)
comment = Comment.find_by_id(cid=cid)
if not comment:
raise Exception("comment by that id not found")
comment.locked = True
@ -321,7 +344,7 @@ class Comment(base):
raise
@classmethod
def add_comment(cls, user_id: int, message: str, replied_to: int, message_id: int = None):
def add_comment(cls, pid: int, user_id: int, message: str, cid: int = None, message_id: int = None):
from flask.ext.login import current_user
from wowfunding.factory import db_session
if not message:
@ -331,9 +354,12 @@ class Comment(base):
raise Exception("no rights to add or modify this comment")
if not message_id:
comment = Comment(user_id=self.id)
if replied_to:
parent = Comment.get(comment_id=comment_id)
proposal = Proposal.find_by_id(pid=pid)
if not proposal:
raise Exception("no proposal by that id")
comment = Comment(user_id=user_id, proposal_id=proposal.id)
if cid:
parent = Comment.find_by_id(cid=cid)
if not parent:
raise Exception("cannot reply to a non-existent comment")
comment.replied_to = parent.id
@ -342,7 +368,7 @@ class Comment(base):
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
raise Exception("no user by that id")
comment = next(c for c in self.comments if c.id == message_id)
comment = next(c for c in user.comments if c.id == message_id)
if comment.locked and not current_user.admin:
raise Exception("your comment has been locked/removed")
except StopIteration:

@ -5,7 +5,7 @@ from flask_yoloapi import endpoint, parameter
import settings
from wowfunding.factory import app, db_session
from wowfunding.orm.orm import Proposal, User
from wowfunding.orm.orm import Proposal, User, Comment
@app.route('/')
@ -25,6 +25,44 @@ def proposal_add():
return make_response(render_template(('proposal_edit.html')))
@app.route('/proposal/comment', methods=['POST'])
@endpoint.api(
parameter('pid', type=int, required=True),
parameter('text', type=str, required=True),
parameter('cid', type=int, required=False)
)
def proposal_comment(pid, text, cid):
if current_user.is_anonymous:
flash('not logged in', 'error')
return redirect(url_for('proposal', pid=pid))
if len(text) <= 3:
flash('comment too short', 'error')
return redirect(url_for('proposal', pid=pid))
try:
Comment.add_comment(user_id=current_user.id, message=text, pid=pid, cid=cid)
except Exception as ex:
flash('Could not add comment: %s' % str(ex), 'error')
return redirect(url_for('proposal', pid=pid))
flash('Comment posted.')
return redirect(url_for('proposal', pid=pid))
@app.route('/proposal/<int:pid>/comment/<int:cid>')
def propsal_comment_reply(cid, pid):
from wowfunding.orm.orm import Comment
c = Comment.find_by_id(cid)
if not c or c.replied_to:
return redirect(url_for('proposal', pid=pid))
p = Proposal.find_by_id(pid)
if not p:
return redirect(url_for('proposals'))
if c.proposal_id != p.id:
return redirect(url_for('proposals'))
return make_response(render_template('comment_reply.html', c=c, pid=pid, cid=cid))
@app.route('/proposal/<int:pid>')
def proposal(pid):
p = Proposal.find_by_id(pid=pid)

@ -360,3 +360,47 @@ textarea.comment{
width: 100%;
max-width: 600px;
}
.votearrow {
width: 10px;
height: 10px;
border: 0px;
margin: 3px 2px 6px;
margin-right: 10px;
margin-top: 7px;
background: url(/static/grayarrow.gif) no-repeat;
}
span.username a{
font-family: Verdana, Geneva, sans-serif;
font-size: 12pt;
color: #828282;
}
.comment-container a.reply{
margin-bottom:2px;
font-family: Verdana, Geneva, sans-serif;
font-size: 11pt;
text-decoration: underline;
color: black;
}
span.date_posted a{
color: #828282;
font-family: Verdana, Geneva, sans-serif;
font-size: 10pt;
margin-left:2px;
}
.comment-container .comment-container{
margin-top: 0.4rem !important;
}
span.username a.author{
color: #008926;
font-weight: bold;
}
:target {
background: linear-gradient(90deg, #ff606008, #ffa93e2b);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block content %}
<!-- Page Content -->
<div class="container" style="margin-bottom:140px;">
{% include 'messages.html' %}
<div class="row">
<!-- Post Content Column -->
<div class="col-lg-12">
<div class="proposal_content">
<!-- Post Content -->
<div class="media mb-4">
<div class="votearrow" title="upvote"></div>
<div class="media-body">
<span class="username"><a href="/user/{{ c.user.username }}">{{c.user.username}}</a></span>
<span class="date_posted">{{c.date_added.strftime('%Y-%m-%d %H:%M')}}</span><br>
{{c.message}}
<br><br>
{% if logged_in %}
<form method="post" action="{{url_for('proposal_comment')}}">
<input type="hidden" name="pid" value="{{pid}}">
<input type="hidden" name="cid" value="{{cid}}">
<textarea class="comment" name="text" rows="6" cols="60"></textarea>
<br><br><input type="submit" value="add comment">
</form>
{% else %}
You need to be logged in to comment.
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /.container -->
{% endblock %}

@ -1,63 +1,61 @@
<div class="row">
<div class="col-md-12">
<div class="card my-6" id="incoming_txs">
<h5 class="card-header">Comments</h5>
<h5 id="comments" class="card-header">Comments</h5>
<div class="card-body">
{% if logged_in %}
<form method="post" action="comment">
<input type="hidden" name="proposal_id" value="{{proposal.id}}">
<input type="hidden" name="pid" value="{{proposal.id}}">
<textarea class="comment" name="text" rows="6" cols="60"></textarea>
<br><br><input type="submit" value="add comment">
</form>
{% else %}
You need to be logged in to comment.<br>
Press <b>F</b> to pay respects.
You need to be logged in to comment.
<br>
{% endif %}
<br>
{% for c in proposal._comments %}
<!-- Single Comment -->
<div class="media mb-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media mb-4 comment-container" id="comment-{{c.id}}">
<div class="votearrow" title="upvote"></div>
<div class="media-body">
<h5 class="mt-0">Commenter Name</h5>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras
purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi
vulputate fringilla. Donec lacinia congue felis in faucibus.
</div>
</div>
<span class="username">
<a class="{% if c.user.username == proposal.user.username %}author{% endif %}" href="/user/{{ c.user.username }}">
{{c.user.username}}
</a>
</span>
<span class="date_posted">
<a href="/proposal/{{proposal.id}}#comment-{{c.id}}">
{{c.date_added.strftime('%Y-%m-%d %H:%M')}}
</a>
</span><br>
{{c.message}}
<br>
<a class="reply" href="{{url_for('propsal_comment_reply', cid=c.id, pid=proposal.id)}}">reply</a>
<!-- Comment with nested comments -->
<div class="media mb-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media-body">
<h5 class="mt-0">Commenter Name</h5>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras
purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi
vulputate fringilla. Donec lacinia congue felis in faucibus.
<div class="media mt-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
{% for _c in c.comments %}
<div class="media mt-4 comment-container" id="comment-{{_c.id}}">
<div class="votearrow" title="upvote"></div>
<div class="media-body">
<h5 class="mt-0">Commenter Name</h5>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin.
Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc
ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.
<span class="username">
<a class="{% if c.user.username == proposal.user.username %}author{% endif %}" href="/user/{{ c.user.username }}">
{{_c.user.username}}
</a>
</span>
<span class="date_posted">
<a href="/proposal/{{proposal.id}}#comment-{{_c.id}}">
{{c.date_added.strftime('%Y-%m-%d %H:%M')}}
</a>
</span><br>
{{_c.message}}
</div>
</div>
<div class="media mt-4">
<img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
<div class="media-body">
<h5 class="mt-0">Commenter Name</h5>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin.
Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc
ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
</div>

@ -1,17 +1,15 @@
{% with messages = get_flashed_messages() %}
{% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %}
<div class="row">
<div class="col-lg-12">
{% for message in messages %}
{% if message.category != 'error' %}
<div class="alert alert-success">
{{ message }}
</div>
{% else %}
<div class="alert alert-danger">
{{ message }}
</div>
{% endif %}
<div class="alert {% if message[0] != 'error' %}alert-success{% else %}alert-danger{% endif %}">
<img src="/static/doge_head.png" style="
width: 64px;
margin-right: 8px;
">
{{ message[1] }}
</div>
{% endfor %}
</div>
</div>

Loading…
Cancel
Save