Upload files to "template"
This commit is contained in:
parent
5a12e7aefa
commit
9a8ed1e29d
45
template/posts_partial.html
Normal file
45
template/posts_partial.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{% for post in posts %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">{{ post.content|safe }}</p>
|
||||||
|
{% if post.image_url %}
|
||||||
|
<img src="{{ url_for('static', filename='uploads/' + post.image_url) }}" class="img-fluid mb-2 post-image" alt="Post image" data-bs-toggle="modal" data-bs-target="#imageModal{{ post.id }}">
|
||||||
|
{% endif %}
|
||||||
|
<div class="d-flex align-items-center mt-2">
|
||||||
|
{% if post.author %}
|
||||||
|
{% if post.author.profile_picture %}
|
||||||
|
<img src="{{ url_for('static', filename='uploads/' + post.author.profile_picture) }}" class="rounded-circle me-2" alt="Profile Picture" style="width: 30px; height: 30px; object-fit: cover;">
|
||||||
|
{% else %}
|
||||||
|
<div class="rounded-circle me-2 d-flex justify-content-center align-items-center bg-primary" style="width: 30px; height: 30px;">
|
||||||
|
<span class="text-white" style="font-size: 0.8rem;">{{ post.author.username[0].upper() }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<small class="text-muted">
|
||||||
|
<a href="{{ url_for('profile', username=post.author.username) }}" class="text-decoration-none">{{ post.author.username }}</a>
|
||||||
|
</small>
|
||||||
|
{% else %}
|
||||||
|
<small class="text-muted">{{ post.anonymous_username }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<p class="card-text"><small class="text-muted">{{ post.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</small></p>
|
||||||
|
<button class="btn btn-primary btn-sm like-btn" data-post-id="{{ post.id }}">
|
||||||
|
<i class="bi bi-heart-fill"></i> Like (<span class="like-count">{{ post.likes|length }}</span>)
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary btn-sm comment-btn" data-post-id="{{ post.id }}">
|
||||||
|
<i class="bi bi-chat-fill"></i> Comment ({{ post.comments|length }})
|
||||||
|
</button>
|
||||||
|
<a href="{{ url_for('post_detail', post_id=post.id) }}" class="btn btn-info btn-sm" target="_blank">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i> Open in new tab
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-info btn-sm copy-link-btn" data-post-url="{{ url_for('post_detail', post_id=post.id, _external=True, _scheme='https') }}">
|
||||||
|
<i class="bi bi-link-45deg"></i> Copy Link
|
||||||
|
</button>
|
||||||
|
{% if (current_user.is_authenticated and post.author and current_user.id == post.author.id) or (not current_user.is_authenticated and post.user_id == session['user_id']) %}
|
||||||
|
<a href="{{ url_for('edit_post', post_id=post.id) }}" class="btn btn-warning btn-sm">
|
||||||
|
<i class="bi bi-pencil-fill"></i> Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<!-- ... (comment section remains the same) ... -->
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
245
template/profile.html
Normal file
245
template/profile.html
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ user.username }}'s Profile{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h1 class="card-title">{{ user.username }}'s Profile</h1>
|
||||||
|
{% if user.profile_picture %}
|
||||||
|
<img src="{{ url_for('static', filename='uploads/' + user.profile_picture) }}" class="img-fluid rounded-circle mb-3" alt="Profile Picture" style="background-color: transparent;">
|
||||||
|
{% else %}
|
||||||
|
<div class="profile-initial rounded-circle mb-3 d-flex justify-content-center align-items-center bg-primary" style="width: 100px; height: 100px;">
|
||||||
|
<span class="text-white" style="font-size: 3rem;">{{ user.username[0].upper() }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<p class="card-text"><strong>Bio:</strong></p>
|
||||||
|
<div class="card-text bio-text">{{ user.rendered_bio|safe or "No bio available." }}</div>
|
||||||
|
<h5 class="card-title">Stats</h5>
|
||||||
|
<p class="card-text">Posts: {{ post_count }}</p>
|
||||||
|
<p class="card-text">Total Likes: {{ like_count }}</p>
|
||||||
|
<p class="card-text">Total Comments: {{ comment_count }}</p>
|
||||||
|
{% if current_user.is_authenticated and current_user.id == user.id %}
|
||||||
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#editProfileModal">Edit Profile</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h2>Posts</h2>
|
||||||
|
{% for post in posts %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">{{ post.content|safe }}</p>
|
||||||
|
{% if post.image_url %}
|
||||||
|
<img src="{{ url_for('static', filename='uploads/' + post.image_url) }}" class="img-fluid mb-2" alt="Post image">
|
||||||
|
{% endif %}
|
||||||
|
<div class="d-flex align-items-center mt-2">
|
||||||
|
{% if post.author.profile_picture %}
|
||||||
|
<img src="{{ url_for('static', filename='uploads/' + post.author.profile_picture) }}" class="rounded-circle me-2" alt="Profile Picture" style="width: 30px; height: 30px; object-fit: cover;">
|
||||||
|
{% else %}
|
||||||
|
<div class="rounded-circle me-2 d-flex justify-content-center align-items-center bg-primary" style="width: 30px; height: 30px;">
|
||||||
|
<span class="text-white" style="font-size: 0.8rem;">{{ post.author.username[0].upper() }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<small class="text-muted">
|
||||||
|
<a href="{{ url_for('profile', username=post.author.username) }}" class="text-decoration-none">{{ post.author.username }}</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<p class="card-text"><small class="text-muted">{{ post.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</small></p>
|
||||||
|
<button class="btn btn-primary btn-sm like-btn" data-post-id="{{ post.id }}">
|
||||||
|
<i class="bi bi-heart-fill"></i> Like (<span class="like-count">{{ post.likes|length }}</span>)
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary btn-sm comment-btn" data-post-id="{{ post.id }}">
|
||||||
|
<i class="bi bi-chat-fill"></i> Comment ({{ post.comments|length }})
|
||||||
|
</button>
|
||||||
|
{% if current_user.is_authenticated and current_user.id == post.author.id %}
|
||||||
|
<a href="{{ url_for('edit_post', post_id=post.id) }}" class="btn btn-warning btn-sm">
|
||||||
|
<i class="bi bi-pencil-fill"></i> Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-footer comment-section" id="comment-section-{{ post.id }}" style="display: none;">
|
||||||
|
<h6>Comments:</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{% for comment in post.comments if not comment.parent %}
|
||||||
|
{% with depth=1 %}
|
||||||
|
{% include 'comment.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<form class="comment-form" data-post-id="{{ post.id }}">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" class="form-control" placeholder="Add a comment" name="content" required>
|
||||||
|
<button class="btn btn-outline-secondary" type="submit">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Profile Modal -->
|
||||||
|
{% if current_user.is_authenticated and current_user.id == user.id %}
|
||||||
|
<div class="modal fade" id="editProfileModal" tabindex="-1" aria-labelledby="editProfileModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editProfileModalLabel">Edit Profile</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form method="POST" enctype="multipart/form-data" id="profileForm">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="bio" class="form-label">Bio</label>
|
||||||
|
<textarea class="form-control" id="bio" name="bio" rows="5">{{ user.bio }}</textarea>
|
||||||
|
<small class="form-text text-muted">Markdown and HTML color styling are supported.</small>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="profile_picture" class="form-label">Profile Picture</label>
|
||||||
|
<input type="file" class="form-control" id="profile_picture" name="profile_picture" accept="image/*">
|
||||||
|
</div>
|
||||||
|
<div id="cropperContainer" style="display: none;">
|
||||||
|
<img id="cropperImage" src="" alt="Image to crop" style="max-width: 100%;">
|
||||||
|
</div>
|
||||||
|
{% if user.profile_picture %}
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="remove_picture" name="remove_picture">
|
||||||
|
<label class="form-check-label" for="remove_picture">Remove current profile picture</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<input type="hidden" name="cropped_data" id="croppedData">
|
||||||
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css">
|
||||||
|
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
let cropper;
|
||||||
|
|
||||||
|
$('#profile_picture').change(function(e) {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(event) {
|
||||||
|
$('#cropperImage').attr('src', event.target.result);
|
||||||
|
$('#cropperContainer').show();
|
||||||
|
if (cropper) {
|
||||||
|
cropper.destroy();
|
||||||
|
}
|
||||||
|
cropper = new Cropper($('#cropperImage')[0], {
|
||||||
|
aspectRatio: 1,
|
||||||
|
viewMode: 1,
|
||||||
|
minCropBoxWidth: 200,
|
||||||
|
minCropBoxHeight: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#profileForm').submit(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (cropper) {
|
||||||
|
const croppedCanvas = cropper.getCroppedCanvas({
|
||||||
|
width: 200,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
$('#croppedData').val(croppedCanvas.toDataURL());
|
||||||
|
}
|
||||||
|
this.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
var easyMDE = new EasyMDE({
|
||||||
|
element: document.getElementById('bio'),
|
||||||
|
spellChecker: false,
|
||||||
|
autofocus: true,
|
||||||
|
lineWrapping: true,
|
||||||
|
toolbar: [
|
||||||
|
"bold", "italic", "heading", "quote", "unordered-list", "ordered-list",
|
||||||
|
"link", "code", "table", "undo", "redo",
|
||||||
|
{
|
||||||
|
name: "custom-color",
|
||||||
|
action: function(editor){
|
||||||
|
var cm = editor.codemirror;
|
||||||
|
var output = '';
|
||||||
|
var selectedText = cm.getSelection();
|
||||||
|
var color = prompt("Enter color (e.g., red, #ff0000, rgb(255,0,0)):");
|
||||||
|
if (color) {
|
||||||
|
output = '<span style="color: ' + color + ';">' + selectedText + '</span>';
|
||||||
|
cm.replaceSelection(output);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
className: "fa fa-paint-brush",
|
||||||
|
title: "Custom Color",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
status: false,
|
||||||
|
theme: "dark"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Adjust line height in the editor
|
||||||
|
easyMDE.codemirror.getWrapperElement().style.lineHeight = "1.3";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
|
||||||
|
<style>
|
||||||
|
.bio-text {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
line-height: 1.3; /* Reduce line spacing */
|
||||||
|
}
|
||||||
|
.bio-text a {
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.bio-text a:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
}
|
||||||
|
.EasyMDEContainer {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.EasyMDEContainer .CodeMirror {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.editor-toolbar {
|
||||||
|
background-color: #444;
|
||||||
|
border-color: #555;
|
||||||
|
}
|
||||||
|
.editor-toolbar button {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.editor-toolbar button:hover,
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #fff;
|
||||||
|
}
|
||||||
|
.CodeMirror-line {
|
||||||
|
padding: 0 !important; /* Remove extra padding */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
18
template/register.html
Normal file
18
template/register.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Register{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4">Register</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Register</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user