add web
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Messages{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/message.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.text-secondary {
|
||||
color: rgb(199, 199, 199) !important;
|
||||
}
|
||||
.message {
|
||||
color: rgb(60,60,60) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<h1 class="m-0">{%if isOutgoing:%}Outgoing {%endif%}Messages</h1>
|
||||
{% if not isOutgoing: %}
|
||||
<a class="m-0 mb-2 text-decoration-none" href="/messages/outgoing" style="font-size: 11px;">Click here to see your outgoing messages</a>
|
||||
{%else%}
|
||||
<a class="m-0 mb-2 text-decoration-none" href="/messages" style="font-size: 11px;">Click here to see your incoming messages</a>
|
||||
{%endif%}
|
||||
<p class="m-0" style="font-size: 12px;">Do not send any personal or sensitive information about yourself! A staff member will never ask for your password or email.</p>
|
||||
<div class="linebreak"></div>
|
||||
<div>
|
||||
{% for message in UserMessages %}
|
||||
<a href="/messages/view/{{message.id}}" class="text-decoration-none">
|
||||
<div class="w-100 border p-2 message mb-2 rounded {% if message.read == False %}border-primary{%endif%}">
|
||||
<div class="d-flex">
|
||||
<img class="overflow-hidden me-2 " width="48" height="48" src="/Thumbs/Avatar.ashx?x=48&y=48&userId={%if not isOutgoing %}{{message.sender.id}}{%else%}{{message.recipient.id}}{%endif%}" alt="{%if not isOutgoing %}{{message.sender.username}}{%else%}{{message.recipient.username}}{%endif%}"/>
|
||||
<div class="overflow-hidden w-100">
|
||||
<div class="d-flex align-items-center w-100">
|
||||
<b class="text-white">{%if not isOutgoing %}{{message.sender.username}}{%else%}{{message.recipient.username}}{%endif%}</b>
|
||||
<p class="m-0 text-secondary ms-auto" style="font-size:small;">{{message.created.strftime("%b %d, %Y %I:%M %p")}} UTC</p>
|
||||
</div>
|
||||
<p class="text-white m-0 text-truncate">{{message.subject}} - <span class="text-secondary fw-normal">{{message.content}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{%endfor%}
|
||||
{% if len(UserMessages.items) == 0: %}
|
||||
<h5 class="mt-2 mb-2 text-center">No results found</h5>
|
||||
{%endif%}
|
||||
</div>
|
||||
<div class="linebreak"></div>
|
||||
<div class="d-flex">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not UserMessages.has_prev %}text-secondary{%endif%}" {% if UserMessages.has_prev %}href="/messages/{%if isOutgoing:%}outgoing{%endif%}?page={{UserMessages.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{UserMessages.page}} of {{UserMessages.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not UserMessages.has_next %}text-secondary{%endif%}" {% if UserMessages.has_next %}href="/messages/{%if isOutgoing:%}outgoing{%endif%}?page={{UserMessages.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,148 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort
|
||||
from app.models.messages import Message
|
||||
from app.util import auth, turnstile, websiteFeatures
|
||||
from app.extensions import db, csrf, limiter, redis_controller
|
||||
from app.models.user import User
|
||||
from app.util.textfilter import FilterText
|
||||
from sqlalchemy import or_
|
||||
|
||||
MessageRoute = Blueprint('message', __name__, template_folder="pages")
|
||||
|
||||
def CreateSystemMessage( subject : str, message : str, userid : int ):
|
||||
"""
|
||||
Creates a system message from the system user to the specified user
|
||||
"""
|
||||
TargetUser = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
return False
|
||||
NewMessage : Message = Message(
|
||||
subject=subject,
|
||||
content=message,
|
||||
sender_id=1,
|
||||
recipient_id=userid
|
||||
)
|
||||
db.session.add(NewMessage)
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
@MessageRoute.route("/my/messages", methods=["GET"])
|
||||
@auth.authenticated_required
|
||||
def my_messages():
|
||||
return redirect("/messages")
|
||||
|
||||
@MessageRoute.route("/new/<userid>", methods=["GET"])
|
||||
@auth.authenticated_required
|
||||
def new_message(userid):
|
||||
TargetUser = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
return "User not found",404
|
||||
if TargetUser.accountstatus == 4 or TargetUser.accountstatus == 3:
|
||||
abort(404)
|
||||
|
||||
AuthenticatedUser = auth.GetCurrentUser()
|
||||
if AuthenticatedUser.id == TargetUser.id:
|
||||
return "You cannot message yourself",400
|
||||
|
||||
MessageReplyContext = request.args.get("reply", default=None, type=int)
|
||||
if MessageReplyContext is not None:
|
||||
ReplyMessage = Message.query.filter_by( id = MessageReplyContext ).filter( or_( Message.sender_id == AuthenticatedUser.id, Message.recipient_id == AuthenticatedUser.id ) ).first()
|
||||
else:
|
||||
ReplyMessage = None
|
||||
|
||||
return render_template("messages/new.html", targetuser=TargetUser, replymessage=ReplyMessage)
|
||||
|
||||
@MessageRoute.route("/new/<userid>", methods=["POST"])
|
||||
@auth.authenticated_required
|
||||
@limiter.limit("10/minute")
|
||||
def send_message(userid):
|
||||
if 'cf-turnstile-response' not in request.form or request.form.get('cf-turnstile-response') == '':
|
||||
flash("Please complete the captcha", "error")
|
||||
return redirect(f"/messages/new/{userid}")
|
||||
if 'message' not in request.form or request.form.get('message') == '':
|
||||
flash("Please enter a message", "error")
|
||||
return redirect(f"/messages/new/{userid}")
|
||||
if 'subject' not in request.form or request.form.get('subject') == '':
|
||||
flash("Please enter a subject", "error")
|
||||
return redirect(f"/messages/new/{userid}")
|
||||
if not websiteFeatures.GetWebsiteFeature("MessageSending"):
|
||||
flash("Message sending is temporarily disabled", "error")
|
||||
return redirect(f"/messages/new/{userid}")
|
||||
TargetUser : User | None = User.query.filter_by(id=userid).first()
|
||||
if TargetUser is None:
|
||||
abort(404)
|
||||
if TargetUser.accountstatus == 4 or TargetUser.accountstatus == 3:
|
||||
abort(404)
|
||||
|
||||
AuthenticatedUser = auth.GetCurrentUser()
|
||||
if AuthenticatedUser.id == TargetUser.id:
|
||||
abort(400)
|
||||
|
||||
if len(request.form.get("message")) > 1024:
|
||||
flash("Your message is too long", "error")
|
||||
return redirect(f"/messages/new/{TargetUser.id}")
|
||||
if len(request.form.get('subject')) > 64:
|
||||
flash("Your subject is too long", "error")
|
||||
return redirect(f"/messages/new/{TargetUser.id}")
|
||||
|
||||
if not turnstile.VerifyToken(request.form.get('cf-turnstile-response')):
|
||||
flash("Invalid captcha", "error")
|
||||
return redirect(f"/messages/new/{TargetUser.id}")
|
||||
if redis_controller.get(f"message:{TargetUser.id}:{AuthenticatedUser.id}") is not None:
|
||||
flash("You are sending messages too quickly", "error")
|
||||
return redirect(f"/messages/new/{TargetUser.id}")
|
||||
NewLineCount = request.form.get('message').count("\n")
|
||||
if NewLineCount > 10:
|
||||
flash("Your message has too many newlines", "error")
|
||||
return redirect(f"/messages/new/{TargetUser.id}")
|
||||
redis_controller.set(f"message:{TargetUser.id}:{AuthenticatedUser.id}", "1", ex=60)
|
||||
|
||||
FilteredSubject = FilterText(request.form.get('subject'))
|
||||
FilteredMessage = FilterText(request.form.get('message'))
|
||||
|
||||
NewMessage = Message(
|
||||
sender_id=AuthenticatedUser.id,
|
||||
recipient_id=TargetUser.id,
|
||||
subject=FilteredSubject,
|
||||
content=FilteredMessage
|
||||
)
|
||||
db.session.add(NewMessage)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(f"/messages/view/{NewMessage.id}")
|
||||
|
||||
@MessageRoute.route("/view/<messageid>", methods=["GET"])
|
||||
@auth.authenticated_required
|
||||
def view_message(messageid):
|
||||
MessageObj : Message = Message.query.filter_by(id=messageid).first()
|
||||
if MessageObj is None:
|
||||
abort(404)
|
||||
AuthenicatatedUser = auth.GetCurrentUser()
|
||||
if AuthenicatatedUser.id != MessageObj.sender_id and AuthenicatatedUser.id != MessageObj.recipient_id:
|
||||
abort(404)
|
||||
if AuthenicatatedUser.id == MessageObj.recipient_id:
|
||||
MessageObj.read = True
|
||||
db.session.commit()
|
||||
SenderObj = User.query.filter_by(id=MessageObj.sender_id).first()
|
||||
RecieverObj = User.query.filter_by(id=MessageObj.recipient_id).first()
|
||||
MessageCreated = MessageObj.created.strftime("%b %d, %Y %I:%M %p")
|
||||
|
||||
messagelines = MessageObj.content.split("\n")
|
||||
return render_template("messages/view.html", message=MessageObj, created=MessageCreated,sender=SenderObj, reciever=RecieverObj, messagelines=messagelines)
|
||||
|
||||
@MessageRoute.route("/", methods=['GET'])
|
||||
@auth.authenticated_required
|
||||
def messages():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
PageNumber = max( request.args.get('page', default=1, type=int), 1 )
|
||||
UserMessagesList = Message.query.filter_by(recipient_id = AuthenticatedUser.id).order_by(Message.created.desc()).paginate(page = PageNumber, per_page = 10, error_out = False)
|
||||
|
||||
return render_template("messages/index.html", UserMessages=UserMessagesList, isOutgoing=False)
|
||||
|
||||
@MessageRoute.route("/outgoing", methods=['GET'])
|
||||
@auth.authenticated_required
|
||||
def outgoing_messages():
|
||||
AuthenticatedUser : User = auth.GetCurrentUser()
|
||||
PageNumber = max( request.args.get('page', default=1, type=int), 1 )
|
||||
UserMessagesList = Message.query.filter_by(sender_id = AuthenticatedUser.id).order_by(Message.created.desc()).paginate(page = PageNumber, per_page = 10, error_out = False)
|
||||
|
||||
return render_template("messages/index.html", UserMessages=UserMessagesList, isOutgoing=True)
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}New Message{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/message.css" rel="stylesheet"/>
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert border border-danger p-2 text-center messagealerts">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<h4>New Message</h4>
|
||||
<div class="input-group mb-2">
|
||||
<span class="input-group-text">Recipient</span>
|
||||
<input class="form-control" type="text" value="{{targetuser.username}}" readonly>
|
||||
</div>
|
||||
<form method="post" class="w-100">
|
||||
<div class="input-group mb-2">
|
||||
<span class="input-group-text">Subject</span>
|
||||
<input class="form-control" type="text" name="subject" {% if not replymessage %}required{%endif%}{% if replymessage %} disabled value='RE: {{replymessage.subject}}'{%endif%}>
|
||||
</div>
|
||||
<textarea class="form-control rounded" placeholder="Message ( 1024 Chars limit )" name="message" style="min-height:200px" required></textarea>
|
||||
<p class="m-0" style="font-size: 12px;">Do not send any personal or sensitive information about yourself! A staff member will never ask for your password or email.</p>
|
||||
<div class="d-flex justify-content-end align-items-top mt-2">
|
||||
<div class="cf-turnstile" data-theme="dark" data-sitekey="{{turnstilekey}}"></div>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="ms-3">
|
||||
<button type="submit" class="btn btn-primary">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}{{message.subject}}{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/message.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.text-secondary {
|
||||
color: rgb(199, 199, 199) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<h3>{{message.subject}}</h3>
|
||||
<p class="text-secondary m-0" style="font-size:12px;">To: <a href="/users/{{reciever.id}}/profile" class="text-white text-decoration-none">{{reciever.username}}</a></p>
|
||||
<div class="linebreak"></div>
|
||||
<a href="/users/{{sender.id}}/profile" class="text-decoration-none text-white">
|
||||
<div class="d-flex">
|
||||
<img class="overflow-hidden me-2 " width="60" height="60" src="/Thumbs/Avatar.ashx?x=60&y=60&userId={{sender.id}}" alt="{{sender.username}}"/>
|
||||
<div>
|
||||
<b>{{sender.username}}</b>
|
||||
<p class="m-0 text-secondary">{{created}} UTC</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% for line in messagelines: %}
|
||||
{% if line.strip() != '' %}
|
||||
<p class="text-secondary m-0">{{line}}</p>
|
||||
{%else%}
|
||||
<br>
|
||||
{%endif%}
|
||||
{% endfor %}
|
||||
<div class="linebreak"></div>
|
||||
<div class="d-flex">
|
||||
<a class="btn btn-border border-primary p-1 ps-2 pe-2 btn-sm mb-2 me-2" href="/messages">Back to messages</a>
|
||||
{% if sender.id != currentuser.id %}
|
||||
<a class="btn btn-border border-success p-1 ps-2 pe-2 btn-sm mb-2" href="/messages/new/{{sender.id}}?reply={{message.id}}">Reply</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user