from flask import Flask, render_template, redirect, request, session, url_for, g from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import relationship from flask_admin import Admin, AdminIndexView, expose, BaseView from flask_admin.contrib.sqla import ModelView from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed from wtforms import StringField, TextAreaField, SelectField from wtforms.validators import InputRequired from werkzeug.utils import secure_filename from functools import wraps from datetime import datetime import os import logging app = Flask(__name__) app.secret_key = 'bebra' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Picture_Puzzle_web.db' app.config['UPLOAD_FOLDER'] = 'uploads' app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif', 'mp4', 'waw'} db = SQLAlchemy(app) logging.basicConfig(level=logging.DEBUG) class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(100), unique=True, nullable=False) password = db.Column(db.String(100), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) class Post(db.Model): __tablename__ = 'post' id = db.Column(db.Integer, primary_key=True) date_created = db.Column(db.String(100), unique=False, nullable=False) alias = db.Column(db.String(100), unique=True, nullable=False) title = db.Column(db.String(100), nullable=False) image = db.Column(db.String(100), nullable=False) class ForumCategory(db.Model): __tablename__ = 'forumcategory' id = db.Column(db.Integer, primary_key=True) category_name = db.Column(db.String(100), nullable=False) description = db.Column(db.String(200)) class ForumPost(db.Model): __tablename__ = 'forumpost' id = db.Column(db.Integer, primary_key=True) category_id = db.Column(db.Integer, db.ForeignKey('forumcategory.id'), nullable=False) post_name = db.Column(db.String(100), nullable=False) created_by = db.Column(db.String(100), nullable=False) creation_date = db.Column(db.DateTime, default=datetime.now) media = db.relationship('Media', backref='forumpost', lazy=True) text = db.Column(db.Text, nullable=False) edited = db.Column(db.Boolean, default=False) class ForumComment(db.Model): __tablename__ = 'forumcomment' id = db.Column(db.Integer, primary_key=True) post_id = db.Column(db.Integer, db.ForeignKey('forumpost.id'), nullable=False) created_by = db.Column(db.String(100), nullable=False) creation_date = db.Column(db.DateTime, nullable=False) media = db.Column(db.Integer) # Assuming 'media' is a column containing media IDs text = db.Column(db.Text, nullable=False) edited = db.Column(db.Boolean, default=False) # Define a primaryjoin expression post = relationship("ForumPost", primaryjoin="foreign(ForumComment.post_id) == remote(ForumPost.id)") class Media(db.Model): __tablename__ = 'media' id = db.Column(db.Integer, primary_key=True) post_id = db.Column(db.Integer, db.ForeignKey('forumpost.id'), nullable=False) filename = db.Column(db.String(100), nullable=False) class CreatePostForm(FlaskForm): category_id = SelectField('Category', validators=[InputRequired()], coerce=int) post_name = StringField('Post Title', validators=[InputRequired()]) created_by = StringField('Your Name', validators=[InputRequired()]) text = TextAreaField('Post Content', validators=[InputRequired()]) media = FileField('Insert Media', validators=[FileAllowed(app.config['ALLOWED_EXTENSIONS'])]) def admin_login_required(view_func): @wraps(view_func) def decorated_function(*args, **kwargs): if not session.get('admin_logged_in'): return redirect(url_for('admin_login')) return view_func(*args, **kwargs) return decorated_function class MyAdminIndexView(AdminIndexView): @expose('/') @admin_login_required def index(self): return self.render('admin/index.html') class UserAdminView(ModelView): column_exclude_list = ['password'] can_export = True export_types = ['csv'] class PostAdminView(ModelView): can_export = True export_types = ['csv'] class ForumCategoryAdminView(ModelView): can_export = True export_types = ['csv'] class ForumPostAdminView(ModelView): can_export = True export_types = ['csv'] class ForumCommentAdminView(ModelView): can_export = True export_types = ['csv'] class LogoutView(BaseView): @expose('/') def index(self): session.pop("admin_logged_in", None) return redirect(url_for("index")) admin = Admin(app, name='Admin Panel', template_mode='bootstrap3', index_view=MyAdminIndexView()) admin.add_view(UserAdminView(User, db.session)) admin.add_view(PostAdminView(Post, db.session)) admin.add_view(ForumCategoryAdminView(ForumCategory, db.session)) admin.add_view(ForumPostAdminView(ForumPost, db.session)) admin.add_view(ForumCommentAdminView(ForumComment, db.session)) admin.add_view(LogoutView(name='Logout', endpoint='admin_logout')) @app.before_request def check_admin_login(): if request.path.startswith('/admin/') and not session.get('admin_logged_in'): if request.path != '/admin/login' and request.path != '/admin/logout': return redirect(url_for('admin_login')) ADMIN_USERNAME = 'user' ADMIN_PASSWORD = '1234321' @app.before_request def before_request(): g.user = None if 'username' in session: user = User.query.filter_by(username=session['username']).first() g.user = user @app.route("/admin/login", methods=["GET", "POST"]) def admin_login(): if request.method == "POST": username = request.form["username"] password = request.form["password"] if username == ADMIN_USERNAME and password == ADMIN_PASSWORD: session["admin_logged_in"] = True return redirect(url_for("admin.index")) else: return render_template("admin/login.html", error_msg="Invalid credentials") return render_template("admin/login.html", error_msg=None) @app.route("/admin/logout") def admin_logout(): session.pop("admin_logged_in", None) return redirect(url_for("admin_login")) @app.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": username = request.form["username"] email = request.form["email"] password = request.form["password"] existing_user = User.query.filter_by(email=email).first() if existing_user: error_msg = "Email already exists" return render_template("auth/register.html", error_msg=error_msg) existing_username = User.query.filter_by(username=username).first() if existing_username: error_msg = "Username already exists" return render_template("auth/register.html", error_msg=error_msg) new_user = User(username=username, email=email, password=password) # type: ignore db.session.add(new_user) db.session.commit() return redirect(url_for("login")) return render_template("auth/register.html") @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form["username"] password = request.form["password"] user = User.query.filter_by(username=username, password=password).first() if user: session["username"] = username return redirect(url_for("index")) return render_template("auth/login.html") @app.route("/logout") def logout(): session.pop("username", None) return redirect(url_for("index")) @app.route("/") def index(): latest_posts = Post.query.all()[::-1] return render_template("index.html", latest_posts=latest_posts) @app.route("/posts") def all_posts(): all_posts = Post.query.all()[::-1] return render_template("posts.html", posts=all_posts) @app.route("/posts/") def post(alias): post_info = Post.query.filter_by(alias=alias).first() if post_info: return render_template(f"{alias}.html", post_info=post_info) else: return "Post not found", 404 @app.route('/forums') def forums(): categories = ForumCategory.query.all() return render_template('forums.html', categories=categories) @app.route('/forums/') def category(category_name): category_name = category_name.capitalize() print("Received category name:", category_name) category = ForumCategory.query.filter_by(category_name=category_name).first() if category: posts = ForumPost.query.filter_by(category_id=category.id).limit(10).all() return render_template('category.html', category=category, posts=posts) else: return "Category not found", 404 @app.route('/forums//create_post', methods=['GET', 'POST']) def create_post(category_id): form = CreatePostForm() category = ForumCategory.query.get_or_404(category_id) # Provide choices for category_id field form.category_id.choices = [(category.id, category.category_name) for category in ForumCategory.query.all()] if form.validate_on_submit(): post_name = form.post_name.data created_by = form.created_by.data text = form.text.data media_files = request.files.getlist('media') media_filenames = [] for file in media_files: if file: filename = secure_filename(file.filename) # type: ignore file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) media_filenames.append(filename) new_post = ForumPost( category_id=category_id, post_name=post_name, created_by=created_by, text=text, creation_date=datetime.now(), edited=False ) # type: ignore db.session.add(new_post) db.session.commit() for filename in media_filenames: new_media = Media(post_id=new_post.id, filename=filename) # type: ignore db.session.add(new_media) db.session.commit() return redirect(url_for('category', category_id=category_id)) return render_template('create_post.html', form=form, category=category) @app.route('/forums/', methods=['GET', 'POST']) def view_post(post_id): post = ForumPost.query.get_or_404(post_id) comments = ForumComment.query.filter_by(post_id=post_id).all() if request.method == 'POST': created_by = request.form['created_by'] text = request.form['text'] new_comment = ForumComment(post_id=post_id, created_by=created_by, text=text) # type: ignore db.session.add(new_comment) db.session.commit() return redirect(url_for('view_post', post_id=post_id)) return render_template('post.html', post=post, comments=comments) @app.route('/forums/upvote_post/') def upvote_post(post_id): post = ForumPost.query.get_or_404(post_id) post.upvotes += 1 db.session.commit() return redirect(url_for('post', post_id=post_id)) @app.route('/forums/downvote_post/') def downvote_post(post_id): post = ForumPost.query.get_or_404(post_id) post.downvotes += 1 db.session.commit() return redirect(url_for('post', post_id=post_id)) @app.route('/forums/upvote_comment/') def upvote_comment(comment_id): comment = ForumComment.query.get_or_404(comment_id) comment.upvotes += 1 db.session.commit() return redirect(url_for('post', post_id=comment.post_id)) @app.route('/forums/downvote_comment/') def downvote_comment(comment_id): comment = ForumComment.query.get_or_404(comment_id) comment.downvotes += 1 db.session.commit() return redirect(url_for('post', post_id=comment.post_id)) @app.route("/about") def about(): return render_template("about.html") if __name__ == '__main__': app.run(debug=True)