From 155f90340a845c9fc4519ca7fb45a18648be56ec Mon Sep 17 00:00:00 2001 From: Ben Mosley Date: Mon, 14 Apr 2025 11:31:32 -0500 Subject: [PATCH] First Commit --- .DS_Store | Bin 0 -> 6148 bytes app.py | 320 +++++++++++++++++++++++++++++ templates/about.html | 52 +++++ templates/admin.html | 100 +++++++++ templates/base.html | 85 ++++++++ templates/blog.html | 38 ++++ templates/blog_tag_results.html | 35 ++++ templates/contact.html | 58 ++++++ templates/edit_blog.html | 29 +++ templates/edit_project.html | 26 +++ templates/index.html | 46 +++++ templates/login.html | 66 ++++++ templates/new_blog.html | 78 +++++++ templates/new_project.html | 78 +++++++ templates/project.html | 38 ++++ templates/project_tag_results.html | 35 ++++ templates/resume.html | 95 +++++++++ templates/view_blog.html | 78 +++++++ templates/view_project.html | 77 +++++++ 19 files changed, 1334 insertions(+) create mode 100644 .DS_Store create mode 100644 app.py create mode 100644 templates/about.html create mode 100644 templates/admin.html create mode 100644 templates/base.html create mode 100644 templates/blog.html create mode 100644 templates/blog_tag_results.html create mode 100644 templates/contact.html create mode 100644 templates/edit_blog.html create mode 100644 templates/edit_project.html create mode 100644 templates/index.html create mode 100644 templates/login.html create mode 100644 templates/new_blog.html create mode 100644 templates/new_project.html create mode 100644 templates/project.html create mode 100644 templates/project_tag_results.html create mode 100644 templates/resume.html create mode 100644 templates/view_blog.html create mode 100644 templates/view_project.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..38d284ae6a5f5bddf3b55984a0bc02e964bb8693 GIT binary patch literal 6148 zcmeHKu};H447E!HtynrT<_jW~kttMRV6NCI0c}x1N|Z>Hfh}LaFYq@^%=`{K`z(~u zv=c%ovMb-a`0Ts-U33={5l^316QVH@4WWXA5tanUUqnB@q_GRI|JE*E*Zz_U!~ z^8Xo-y^AQJH9gSg$NrsTEcsN?L=NeeD*O`YbK94(|8&;y9npsFsDNjVDCs}8Bj^p8Gv-lEJhd+YF;*6;I=H~HtCo~wo@y z^oapG*M`S^O3HI_2AqLkVL;A@7%G?=)&ceCK%*r9FoIbHef{(Y#$o`bhIK$#ARI!0 zA(XAeV5=NBgkwB4|2kj@C)SlQkFCsBp|BMm9IHEV8qiy3z!_*Wa1b8Ha{piKuK(LX z{^Sfe1HEE^qimK1!uVVPhReS") + category = request.form['category'] + tags = request.form['tags'] + pinned = 'pinned' in request.form + images = request.files.getlist('images') + + slug = slugify(title) + image_filenames = [secure_filename(image.filename) for image in images if image.filename] + for image, filename in zip(images, image_filenames): + image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + + new_post = BlogPost( + title=title, + slug=slug, + content=content, + category=category, + tags=tags, + pinned=pinned, + images=json.dumps(image_filenames) + ) + db.session.add(new_post) + db.session.commit() + return redirect(url_for('blog')) + + return render_template('new_blog.html') + +@app.route('/newproject', methods=['GET', 'POST']) +@login_required +def new_project(): + if request.method == 'POST': + title = request.form['title'] + content = request.form['content'].replace("\n", "
") + category = request.form['category'] + tags = request.form['tags'] + pinned = 'pinned' in request.form + images = request.files.getlist('images') + + slug = slugify(title) + image_filenames = [secure_filename(image.filename) for image in images if image.filename] + for image, filename in zip(images, image_filenames): + image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + + new_post = ProjectPost( + title=title, + slug=slug, + content=content, + category=category, + tags=tags, + pinned=pinned, + images=json.dumps(image_filenames) + ) + db.session.add(new_post) + db.session.commit() + return redirect(url_for('projects')) + + return render_template('new_project.html') + +@app.route('/edit-blog/', methods=['GET', 'POST']) +@login_required +def edit_blog(slug): + blogpost = BlogPost.query.filter_by(slug=slug).first_or_404() + + if request.method == 'POST': + blogpost.title = request.form['title'] + blogpost.slug = slugify(blogpost.title) + blogpost.content = request.form['content'].replace("\n", "
") + blogpost.category = request.form['category'] + blogpost.tags = request.form['tags'] + blogpost.pinned = 'pinned' in request.form + + images = request.files.getlist('images') + image_filenames = json.loads(blogpost.images) if blogpost.images else [] + + for image in images: + if image.filename: + filename = secure_filename(image.filename) + image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + image_filenames.append(filename) + + blogpost.images = json.dumps(image_filenames) + db.session.commit() + return redirect(url_for('view_blog', slug=blogpost.slug)) + + return render_template('edit_blog.html', blogpost=blogpost) + +@app.route('/edit-project/', methods=['GET', 'POST']) +@login_required +def edit_project(slug): + projectpost = ProjectPost.query.filter_by(slug=slug).first_or_404() + + if request.method == 'POST': + projectpost.title = request.form['title'] + projectpost.slug = slugify(projectpost.title) + projectpost.content = request.form['content'].replace("\n", "
") + projectpost.category = request.form['category'] + projectpost.tags = request.form['tags'] + projectpost.pinned = 'pinned' in request.form + + images = request.files.getlist('images') + image_filenames = json.loads(projectpost.images) if projectpost.images else [] + + for image in images: + if image.filename: + filename = secure_filename(image.filename) + image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + image_filenames.append(filename) + + projectpost.images = json.dumps(image_filenames) + db.session.commit() + return redirect(url_for('view_project', slug=projectpost.slug)) + + return render_template('edit_project.html', projectpost=projectpost) + +@app.route('/delete-blog/', methods=['POST']) +@login_required +def delete_blog(slug): + blogpost = BlogPost.query.filter_by(slug=slug).first_or_404() + if blogpost.images: + for filename in json.loads(blogpost.images): + image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + if os.path.exists(image_path): + os.remove(image_path) + db.session.delete(blogpost) + db.session.commit() + return redirect(url_for('admin_panel')) + +@app.route('/delete-project/', methods=['POST']) +@login_required +def delete_project(slug): + projectpost = ProjectPost.query.filter_by(slug=slug).first_or_404() + if projectpost.images: + for filename in json.loads(projectpost.images): + image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + if os.path.exists(image_path): + os.remove(image_path) + db.session.delete(projectpost) + db.session.commit() + return redirect(url_for('admin_panel')) + +@app.route('/blogtag/') +def view_tag(tag): + tag_lower = tag.lower() # Normalize case + blogpost = BlogPost.query.filter(func.lower(BlogPost.tags).contains(tag_lower)).order_by(BlogPost.id.desc()).all() + return render_template('blog_tag_results.html', blogpost=blogpost, tag=tag) + +@app.route('/projecttag/') +def view_project_tag(tag): + tag_lower = tag.lower() # Normalize case + projectpost = ProjectPost.query.filter(func.lower(ProjectPost.tags).contains(tag_lower)).order_by(ProjectPost.id.desc()).all() + return render_template('project_tag_results.html', projectpost=projectpost, tag=tag) + +@app.route('/blog') +def blog(): + blogpost = BlogPost.query.order_by(BlogPost.id.desc()).all() + return render_template('blog.html', blogpost=blogpost) + +@app.route('/projects') +def projects(): + projectpost = ProjectPost.query.order_by(ProjectPost.id.desc()).all() + return render_template('project.html', projectpost=projectpost) + +@app.route('/blog/') +def view_blog(slug): + blogpost = BlogPost.query.filter_by(slug=slug).first_or_404() + return render_template('view_blog.html', blogpost=blogpost) + +@app.route('/project/') +def view_project(slug): + projectpost = ProjectPost.query.filter_by(slug=slug).first_or_404() + return render_template('view_project.html', projectpost=projectpost) + +@app.route("/contact", methods=["GET", "POST"]) +def contact(): + if request.method == "POST": + name = request.form.get("name") + email = request.form.get("email") + message = request.form.get("message") + + # Compose the email + msg = Message(subject=f"New Contact Message from {name}", + sender=("Portfolio Updates", "ben@bennyshouse.net"), + recipients=["ben@bennyshouse.net"], + body=f"Name: {name}\nEmail: {email}\n\n{message}") + + try: + mail.send(msg) + flash("Your message has been sent!", "success") + except Exception as e: + flash("Something went wrong. Please try again later.", "error") + print(f"Email error: {e}") + + return redirect(url_for("contact")) + + return render_template("contact.html") + + +@app.route('/resume') +def resume(): + return render_template('resume.html') + +@app.route('/about') +def about(): + return render_template('about.html') + +@app.route('/') +def index(): + return render_template('index.html') + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 0000000..4dc52fe --- /dev/null +++ b/templates/about.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} + +{% block title %}About{% endblock %} + +{% block content %} + + +
+

About Me

+

Learn more about who I am, what I do, and what drives me.

+
+ + +
+
+ + +
+

Who Am I?

+

+ I'm a passionate developer and tech enthusiast focused on building useful, secure, and accessible digital experiences. + Whether it’s creating full-stack web applications, designing clean UIs, or managing network infrastructure — I love what I do. +

+
+ + +
+

Skills & Tools

+
    +
  • Python & Flask
  • +
  • HTML CSS and Javascript
  • +
  • SQL (MYSQL, SQLite)
  • +
  • Linux system administration
  • +
  • Networking & Security (WireGuard, Samba, DNS-Server)
  • +
  • Git & Deployment
  • +
  • API integrations
  • +
+
+ + +
+

What I'm About

+

+ I believe technology should empower people — not confuse them. I enjoy creating tools that are intuitive, helpful, and secure. + I'm always learning, whether it’s diving into cybersecurity, mastering backend frameworks, or exploring automation and DevOps. +

+
+ +
+
+ +{% endblock %} diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..41eca6f --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,100 @@ +{% extends "base.html" %} + +{% block title %}Admin{% endblock %} + +{% block content %} + +
+

Admin Dashboard

+

Manage your blog posts and projects here.

+
+ +
+ + +
+
+

Project Options

+ + New Project +
+ +
+ + + + + + + + + + {% for projectpost in projectpost %} + + + + + + {% endfor %} + +
TitleCategoryActions
{{ projectpost.title }}{{ projectpost.category }} + View + Edit +
+ +
+
+
+
+ + +
+
+

Blog Options

+ + New Blog Post +
+ +
+ + + + + + + + + + {% for blogpost in blogpost %} + + + + + + {% endfor %} + +
TitleCategoryActions
{{ blogpost.title }}{{ blogpost.category }} + View + Edit +
+ +
+
+
+
+ +
+ +{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..bc12ccb --- /dev/null +++ b/templates/base.html @@ -0,0 +1,85 @@ + + + + + + + + + + {% block title %}Benjamin Mosley{% endblock %} + + + + + + + + + + + +
+
+ +

+ Benjamin Mosley +

+ + + + + + +
+ + +
+ +
+
+ + + + + +
+ {% block content %}{% endblock %} +
+ + + + + \ No newline at end of file diff --git a/templates/blog.html b/templates/blog.html new file mode 100644 index 0000000..e06edae --- /dev/null +++ b/templates/blog.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block title %}Blog{% endblock %} + +{% block content %} + + +
+

Blog

+

Insights, tutorials, and personal thoughts — straight from my mind to the page.

+
+ + +
+
+
+ {% for blogpost in blogpost %} +
+

+ {{ blogpost.title }} +

+

{{ blogpost.category }}

+ +
+ {% for tag in blogpost.tags.split(',') %} + + #{{ tag.strip() }} + + {% endfor %} +
+
+ {% endfor %} +
+
+
+ +{% endblock %} diff --git a/templates/blog_tag_results.html b/templates/blog_tag_results.html new file mode 100644 index 0000000..b4add6a --- /dev/null +++ b/templates/blog_tag_results.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} + +{% block title %}Tag Results{% endblock %} + +{% block content %} + + +
+

Posts Tagged: #{{ tag }}

+

Here are all blog posts with this tag.

+
+ + +
+
+ {% if blogpost %} +
+ {% for blogpost in blogpost %} +
+

+ + {{ blogpost.title }} + +

+

{{ blogpost.category }}

+
+ {% endfor %} +
+ {% else %} +

No posts found with this tag.

+ {% endif %} +
+
+ +{% endblock %} diff --git a/templates/contact.html b/templates/contact.html new file mode 100644 index 0000000..f976ac2 --- /dev/null +++ b/templates/contact.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} + +{% block title %}Contact{% endblock %} + +{% block content %} + +
+

Contact Me

+

Looking to connect? Here's where you can reach me!

+
+ +
+
+
+ + +
+

Get in Touch

+

Feel free to drop me a message via the form or through any of the channels below:

+ +
+ + + + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+
+
+ +{% endblock %} diff --git a/templates/edit_blog.html b/templates/edit_blog.html new file mode 100644 index 0000000..5307d60 --- /dev/null +++ b/templates/edit_blog.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% block title %}Edit Blog{% endblock %} + +{% block content %} + +
+ + + + + + + + + + + + + + + + + +
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/edit_project.html b/templates/edit_project.html new file mode 100644 index 0000000..d2623cb --- /dev/null +++ b/templates/edit_project.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% block title %}Edit Project{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + + + + + + +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..67c0f26 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} + +{% block title %}Benjamin Mosley{% endblock %} + +{% block content %} + +
+

Benjamin Mosley's Personal Portfolio

+Benjamin Mosley +

I am Benjamin Mosley, a Computer Information Systems student with +a passion for building, creating, and learning.

+ +
+ +
+
+

Contact Me

+

Have any questions? Reach out to me here

+ +
+ +Learn More +
+ +
+

My Projects

+

Check out my latest projects!

+ +
+ +Learn More +
+ +
+

My Blog

+

Read my latest Blog-Posts!

+ +
+ +Learn More +
+ +
+ +{% endblock %} diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..28db1d7 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,66 @@ + + + + + + + Login + + + + + + \ No newline at end of file diff --git a/templates/new_blog.html b/templates/new_blog.html new file mode 100644 index 0000000..1c582fe --- /dev/null +++ b/templates/new_blog.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} + +{% block title %}New Blog Post{% endblock %} + +{% block content %} + +
+

Create a New Blog Post

+

Share your thoughts, tutorials, or experiences with the world.

+
+ +
+
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ +
+
+ + +
+
+ + + + +{% endblock %} diff --git a/templates/new_project.html b/templates/new_project.html new file mode 100644 index 0000000..3db3169 --- /dev/null +++ b/templates/new_project.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} + +{% block title %}New Project Post{% endblock %} + +{% block content %} + +
+

Create a New Project Post

+

Share your projects with the world.

+
+ +
+
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ +
+
+ + +
+
+ + + + +{% endblock %} diff --git a/templates/project.html b/templates/project.html new file mode 100644 index 0000000..43b0969 --- /dev/null +++ b/templates/project.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block title %}Projects{% endblock %} + +{% block content %} + + +
+

My Projects

+

Here’s a showcase of the things I’ve built or contributed to.

+
+ + + +
+
+
+ {% for projectpost in projectpost %} +
+

+ {{ projectpost.title }} +

+

{{ projectpost.category }}

+
+ {% for tag in projectpost.tags.split(',') %} + + #{{ tag.strip() }} + + {% endfor %} +
+
+ {% endfor %} +
+
+
+ +{% endblock %} diff --git a/templates/project_tag_results.html b/templates/project_tag_results.html new file mode 100644 index 0000000..6351bbf --- /dev/null +++ b/templates/project_tag_results.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} + +{% block title %}Tag Results{% endblock %} + +{% block content %} + + +
+

Posts Tagged: #{{ tag }}

+

Here are all Project Posts with this tag.

+
+ + +
+
+ {% if projectpost %} +
+ {% for projectpost in projectpost %} +
+

+ + {{ projectpost.title }} + +

+

{{ projectpost.category }}

+
+ {% endfor %} +
+ {% else %} +

No posts found with this tag.

+ {% endif %} +
+
+ +{% endblock %} diff --git a/templates/resume.html b/templates/resume.html new file mode 100644 index 0000000..8665403 --- /dev/null +++ b/templates/resume.html @@ -0,0 +1,95 @@ +{% extends "base.html" %} + +{% block title %}About Me{% endblock %} + +{% block content %} + + +
+

Benjamin Mosley

+

Aspiring IT Professional & Web Developer

+
+ + +
+
+ + +
+

Email: ben@bennyshouse.net

+

Portfolio: benjaminmosley.com

+

Business Website: bennyshouse.net

+

LinkedIn: Benjamin Mosley

+

GitHub: github.com/Benny-ui-ux

+
+ + +
+

Education

+
    +
  • High School Graduation: Mother of Divine Grace – June 2022
  • +
  • College: West Texas A&M University
    + Currently pursuing B.S. in Computer Information Systems, graduating Spring 2026 +
  • +
+
+ + +
+

Background

+

+ I’ve been working with computers for as long as I can remember — what started as curiosity has grown into a passion. My technical journey continues to evolve through real-world experience and ongoing education. +

+
+ + +
+

Skills

+
    +
  • Python Programming
  • +
  • Linux Operating Systems
  • +
  • Web Design & Hosting
  • +
  • SQL & Database Management
  • +
  • Leadership & Teamwork
  • +
+
+ + +
+

Work Experience

+
+ +
+

United Supermarkets – Borger, TX

+

Grocery Team Lead | Nov 2019 – Aug 2022

+
    +
  • Stocked shelves and maintained floor organization
  • +
  • Assisted customers and team members
  • +
  • Managed ordering and inventory processes
  • +
+
+ +
+

The Pergola Shop – Canyon, TX

+

Wood Stainer / Cutter | Aug 2022 – Aug 2023

+
    +
  • Prepped and treated wood materials for construction
  • +
+
+ +
+

United Supermarkets – Canyon, TX

+

Service Center / Bookkeeper | Aug 2023 – Present

+
    +
  • Nightly accounting duties and managment of my team
  • +
  • Provided high-quality customer service
  • +
+
+ +
+
+ +
+
+ +{% endblock %} diff --git a/templates/view_blog.html b/templates/view_blog.html new file mode 100644 index 0000000..1c97f79 --- /dev/null +++ b/templates/view_blog.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} + +{% block title %}{{ blogpost.title }}{% endblock %} + +{% block content %} + + +
+

{{ blogpost.title }}

+

Category: {{ blogpost.category }}

+
+ {% for tag in blogpost.tags.split(',') %} + + #{{ tag.strip() }} + + {% endfor %} +
+
+ + +
+
+ + {% if blogpost.images %} +
+

Gallery

+ + +
+ {% endif %} + + + + +
+ {{ blogpost.content | safe }} +
+ + + + +
+
+ +{% endblock %} diff --git a/templates/view_project.html b/templates/view_project.html new file mode 100644 index 0000000..5b05123 --- /dev/null +++ b/templates/view_project.html @@ -0,0 +1,77 @@ +{% extends "base.html" %} + +{% block title %}{{ projectpost.title }}{% endblock %} + +{% block content %} + + +
+

{{ projectpost.title }}

+

Category: {{ projectpost.category }}

+
+ {% for tag in projectpost.tags.split(',') %} + + #{{ tag.strip() }} + + {% endfor %} +
+
+ + +
+
+ + {% if projectpost.images %} +
+

Gallery

+ + +
+ {% endif %} + + + +
+ {{ projectpost.content | safe }} +
+ + + + +
+
+ +{% endblock %}