Improved several things, especially /modapp
This commit is contained in:
99
app.py
99
app.py
@@ -9,6 +9,7 @@ import random
|
|||||||
from argon2 import PasswordHasher
|
from argon2 import PasswordHasher
|
||||||
from argon2.exceptions import VerifyMismatchError
|
from argon2.exceptions import VerifyMismatchError
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix # Import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix # Import ProxyFix
|
||||||
|
import logging
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///quotes.db'
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///quotes.db'
|
||||||
@@ -21,6 +22,9 @@ app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_
|
|||||||
# Initialize Argon2 password hasher
|
# Initialize Argon2 password hasher
|
||||||
ph = PasswordHasher()
|
ph = PasswordHasher()
|
||||||
|
|
||||||
|
# Initialize logging for debugging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
# Hardcoded admin credentials (hashed password using Argon2)
|
# Hardcoded admin credentials (hashed password using Argon2)
|
||||||
ADMIN_CREDENTIALS = {
|
ADMIN_CREDENTIALS = {
|
||||||
'username': 'admin',
|
'username': 'admin',
|
||||||
@@ -92,7 +96,7 @@ def vote(id, action):
|
|||||||
else:
|
else:
|
||||||
vote_data = {}
|
vote_data = {}
|
||||||
|
|
||||||
# If user already voted, handle undo or switch vote
|
# If the user has already voted, check for undoing or switching vote
|
||||||
if str(id) in vote_data:
|
if str(id) in vote_data:
|
||||||
previous_action = vote_data[str(id)]
|
previous_action = vote_data[str(id)]
|
||||||
|
|
||||||
@@ -113,12 +117,12 @@ def vote(id, action):
|
|||||||
vote_data[str(id)] = action # Update vote action
|
vote_data[str(id)] = action # Update vote action
|
||||||
flash("Your vote has been changed.", 'success')
|
flash("Your vote has been changed.", 'success')
|
||||||
else:
|
else:
|
||||||
# First time voting on this quote
|
# First-time voting on this quote (starting from neutral)
|
||||||
if action == 'upvote':
|
if action == 'upvote':
|
||||||
quote.votes += 1
|
quote.votes += 1 # Add +1 vote
|
||||||
elif action == 'downvote':
|
elif action == 'downvote':
|
||||||
quote.votes -= 1
|
quote.votes -= 1 # Subtract -1 vote
|
||||||
vote_data[str(id)] = action # Store vote
|
vote_data[str(id)] = action # Store vote action
|
||||||
flash("Thank you for voting!", 'success')
|
flash("Thank you for voting!", 'success')
|
||||||
|
|
||||||
# Save the updated vote data to the cookie
|
# Save the updated vote data to the cookie
|
||||||
@@ -136,15 +140,19 @@ def vote(id, action):
|
|||||||
# Route for displaying a random quote
|
# Route for displaying a random quote
|
||||||
@app.route('/random')
|
@app.route('/random')
|
||||||
def random_quote():
|
def random_quote():
|
||||||
count = Quote.query.filter_by(status=1).count() # Only count approved quotes
|
approved_count = Quote.query.filter_by(status=1).count()
|
||||||
|
pending_count = Quote.query.filter_by(status=0).count()
|
||||||
|
count = Quote.query.count()
|
||||||
|
|
||||||
if count == 0:
|
if count == 0:
|
||||||
flash("No approved quotes available yet.", 'error')
|
flash("No quotes available yet.", 'error')
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
random_offset = random.randint(0, count - 1) # Generate a random offset
|
random_id = random.randint(1, count)
|
||||||
random_quote = Quote.query.filter_by(status=1).offset(random_offset).first() # Fetch a random approved quote
|
random_quote = Quote.query.get(random_id)
|
||||||
|
|
||||||
|
return render_template('random.html', quote=random_quote, approved_count=approved_count, pending_count=pending_count)
|
||||||
|
|
||||||
return render_template('random.html', quote=random_quote)
|
|
||||||
|
|
||||||
@app.route('/<int:id>')
|
@app.route('/<int:id>')
|
||||||
def quote_homepathid(id):
|
def quote_homepathid(id):
|
||||||
@@ -193,54 +201,47 @@ def modapp():
|
|||||||
flash('You need to log in first.', 'danger')
|
flash('You need to log in first.', 'danger')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
all_quotes = Quote.query.order_by(Quote.date.desc()).all()
|
# Apply filtering (pending, approved, rejected)
|
||||||
total_quotes = Quote.query.filter_by(status=1).count() # Count only approved quotes
|
filter_status = request.args.get('filter', 'pending')
|
||||||
|
page = request.args.get('page', 1, type=int)
|
||||||
|
|
||||||
return render_template('modapp.html', all_quotes=all_quotes, total_quotes=total_quotes)
|
if filter_status == 'approved':
|
||||||
|
quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
|
||||||
|
elif filter_status == 'rejected':
|
||||||
|
quotes = Quote.query.filter_by(status=2).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
|
||||||
|
else: # Default to pending
|
||||||
|
quotes = Quote.query.filter_by(status=0).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
|
||||||
|
|
||||||
@app.route('/modapp/bulk_action', methods=['POST'])
|
# Get counts for each status
|
||||||
def bulk_action():
|
approved_count = Quote.query.filter_by(status=1).count()
|
||||||
action = request.form.get('action')
|
pending_count = Quote.query.filter_by(status=0).count()
|
||||||
quote_ids = request.form.getlist('quote_ids')
|
rejected_count = Quote.query.filter_by(status=2).count()
|
||||||
|
|
||||||
if not quote_ids:
|
return render_template('modapp.html', quotes=quotes, filter_status=filter_status,
|
||||||
flash("No quotes selected.", "warning")
|
approved_count=approved_count, pending_count=pending_count,
|
||||||
return redirect(url_for('modapp'))
|
rejected_count=rejected_count)
|
||||||
|
|
||||||
valid_actions = ['approve', 'reject', 'delete']
|
|
||||||
if action not in valid_actions:
|
|
||||||
flash("Invalid action selected.", "error")
|
|
||||||
return redirect(url_for('modapp'))
|
|
||||||
|
|
||||||
if action == 'approve':
|
# Helper function to approve a quote
|
||||||
for quote_id in quote_ids:
|
|
||||||
approve_quote(quote_id)
|
|
||||||
elif action == 'reject':
|
|
||||||
for quote_id in quote_ids:
|
|
||||||
reject_quote(quote_id)
|
|
||||||
elif action == 'delete':
|
|
||||||
for quote_id in quote_ids:
|
|
||||||
delete_quote(quote_id)
|
|
||||||
|
|
||||||
flash(f"Bulk action '{action}' applied to selected quotes.", "success")
|
|
||||||
return redirect(url_for('modapp'))
|
|
||||||
|
|
||||||
# Define helper functions for each action
|
|
||||||
def approve_quote(quote_id):
|
def approve_quote(quote_id):
|
||||||
quote = Quote.query.get(quote_id)
|
quote = Quote.query.get(quote_id)
|
||||||
if quote:
|
if quote and quote.status != 1: # Only approve if not already approved
|
||||||
quote.status = 1 # Approved
|
quote.status = 1 # Approved
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Helper function to reject a quote
|
||||||
def reject_quote(quote_id):
|
def reject_quote(quote_id):
|
||||||
quote = Quote.query.get(quote_id)
|
quote = Quote.query.get(quote_id)
|
||||||
if quote:
|
if quote and quote.status != 2: # Only reject if not already rejected
|
||||||
|
logging.debug(f"Rejecting quote ID: {quote.id}") # Add logging for rejection
|
||||||
quote.status = 2 # Rejected
|
quote.status = 2 # Rejected
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Helper function to delete a quote
|
||||||
def delete_quote(quote_id):
|
def delete_quote(quote_id):
|
||||||
quote = Quote.query.get(quote_id)
|
quote = Quote.query.get(quote_id)
|
||||||
if quote:
|
if quote:
|
||||||
|
logging.debug(f"Deleting quote ID: {quote.id}") # Add logging for deletion
|
||||||
db.session.delete(quote)
|
db.session.delete(quote)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@@ -249,19 +250,31 @@ def search():
|
|||||||
query = request.args.get('q', '').strip() # Get the search query and trim whitespace
|
query = request.args.get('q', '').strip() # Get the search query and trim whitespace
|
||||||
quotes = []
|
quotes = []
|
||||||
|
|
||||||
|
# Query the counts of approved and pending quotes
|
||||||
|
approved_count = Quote.query.filter_by(status=1).count()
|
||||||
|
pending_count = Quote.query.filter_by(status=0).count()
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
# Perform the search only if the query is provided
|
# Perform the search only if the query is provided
|
||||||
quotes = Quote.query.filter(Quote.text.like(f'%{query}%'), Quote.status == 1).all()
|
quotes = Quote.query.filter(Quote.text.like(f'%{query}%'), Quote.status == 1).all()
|
||||||
|
|
||||||
# Render the search page with the results (or empty if no query)
|
# Render the search page with the results, counts, and search query
|
||||||
return render_template('search.html', quotes=quotes, query=query)
|
return render_template('search.html', quotes=quotes, query=query, approved_count=approved_count, pending_count=pending_count)
|
||||||
|
|
||||||
# Route for browsing approved quotes
|
# Route for browsing approved quotes
|
||||||
@app.route('/browse', methods=['GET'])
|
@app.route('/browse', methods=['GET'])
|
||||||
def browse():
|
def browse():
|
||||||
|
# Query the counts of approved and pending quotes
|
||||||
|
approved_count = Quote.query.filter_by(status=1).count()
|
||||||
|
pending_count = Quote.query.filter_by(status=0).count()
|
||||||
|
|
||||||
|
# Pagination setup
|
||||||
page = request.args.get('page', 1, type=int)
|
page = request.args.get('page', 1, type=int)
|
||||||
quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
|
quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
|
||||||
return render_template('browse.html', quotes=quotes)
|
|
||||||
|
# Pass the counts and the quotes to the template
|
||||||
|
return render_template('browse.html', quotes=quotes, approved_count=approved_count, pending_count=pending_count)
|
||||||
|
|
||||||
|
|
||||||
# Approve a quote (admin only)
|
# Approve a quote (admin only)
|
||||||
@app.route('/approve/<int:id>')
|
@app.route('/approve/<int:id>')
|
||||||
|
|||||||
Reference in New Issue
Block a user