Skip to content

Commit 54daf12

Browse files
authored
Merge pull request #37 from codekitchen-community/settings-page
Add settings page to manager
2 parents da6a03a + a12e68d commit 54daf12

File tree

11 files changed

+476
-354
lines changed

11 files changed

+476
-354
lines changed

manager/fw_manager/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pathlib import Path
2+
from datetime import datetime
23

34
from flask_bootstrap import Bootstrap5
45
from flask import Flask
@@ -20,4 +21,10 @@ def create_app():
2021
db.init_app(app)
2122
blueprints.init_app(app)
2223

24+
@app.context_processor
25+
def make_template_context():
26+
return dict(
27+
year=datetime.now().year,
28+
)
29+
2330
return app

manager/fw_manager/blueprints/manager.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os.path
2-
from datetime import datetime
32
from pathlib import Path
43
from uuid import uuid4
54

@@ -12,12 +11,13 @@
1211
from werkzeug.security import check_password_hash
1312

1413
from ..utils import make_resp
15-
from ..models import User, Image, db
16-
from ..forms import UploadImageForm, EditImageForm
14+
from ..models import User, Image, Site, db
15+
from ..forms import UploadImageForm, EditImageForm, SettingsForm
1716

1817
IMG_FOLDER = os.environ.get("IMG_FOLDER_NAME", "img")
1918
THUMBNAIL_FOLDER = os.environ.get("THUMBNAIL_FOLDER_NAME", "thumbnail")
2019
THUMBNAIL_MAX_WIDTH = int(os.environ.get("THUMBNAIL_MAX_WIDTH", 600))
20+
DEFAULT_NO_IMAGE_TIP = os.environ.get("DEFAULT_NO_IMAGE_TIP", "No image.")
2121

2222

2323
manager_bp = Blueprint("manager", __name__)
@@ -69,12 +69,29 @@ def images_page():
6969
return render_template(
7070
"manager.html",
7171
pagination=pagination,
72-
year=datetime.now().year,
7372
add_form=UploadImageForm(),
7473
edit_form=EditImageForm(),
7574
)
7675

7776

77+
@manager_bp.route("/settings", methods=["GET", "POST"])
78+
@auth.login_required
79+
def settings_page():
80+
form = SettingsForm()
81+
site = Site.query.first()
82+
if form.validate_on_submit():
83+
site.title = form.site_title.data
84+
site.description = form.site_description.data
85+
site.no_image_tip = form.no_image_tip.data
86+
db.session.commit()
87+
return redirect(url_for(".images_page"))
88+
if request.method == "GET":
89+
form.site_title.data = site.title
90+
form.site_description.data = site.description
91+
form.no_image_tip.data = site.no_image_tip or DEFAULT_NO_IMAGE_TIP
92+
return render_template("settings.html", form=form)
93+
94+
7895
@manager_bp.post("/images")
7996
@auth.login_required
8097
def add_image():

manager/fw_manager/blueprints/retriever.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import request, Blueprint
22

3-
from ..models import Image
3+
from ..models import db, Image, Site
44

55

66
retriever_bp = Blueprint("retriever", __name__)
@@ -15,4 +15,12 @@ def get_images():
1515
page=page, per_page=page_size, error_out=False
1616
)
1717
images = [p.as_dict() for p in pagination.items]
18-
return {"images": images, "pages": pagination.pages, "total": pagination.total}
18+
site = db.session.scalar(db.select(Site))
19+
return {
20+
"images": images,
21+
"pages": pagination.pages,
22+
"total": pagination.total,
23+
"site_title": site.title,
24+
"site_description": site.description,
25+
"no_image_tip": site.no_image_tip,
26+
}

manager/fw_manager/commands.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from flask import Flask
33
from werkzeug.security import generate_password_hash
44

5-
from .models import db, User
5+
from .models import db, User, Site
66

77

88
@click.command(name="create-tables")
@@ -27,6 +27,8 @@ def create_tables(username, password) -> None:
2727
click.echo("Creating admin...")
2828
user = User(username=username, password_hash=generate_password_hash(password))
2929
db.session.add(user)
30+
site = Site(title="Fine Weather")
31+
db.session.add(site)
3032
click.echo("Done.")
3133
db.session.commit()
3234

manager/fw_manager/forms.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask_wtf import FlaskForm
22
from flask_wtf.file import FileRequired
3-
from wtforms.fields.simple import StringField, FileField
3+
from wtforms.fields.simple import StringField, FileField, SubmitField, TextAreaField
44
from wtforms.validators import InputRequired, Length
55

66
ALLOWED_IMAGE_TYPES = ["jpeg", "gif", "png"]
@@ -31,3 +31,19 @@ class EditImageForm(FlaskForm):
3131
position = StringField("Position")
3232
description = StringField("Description")
3333
time = StringField("Time")
34+
35+
36+
class SettingsForm(FlaskForm):
37+
site_title = StringField(
38+
"Site title",
39+
validators=[InputRequired(), Length(1, 100)],
40+
)
41+
site_description = TextAreaField(
42+
"Site description",
43+
validators=[InputRequired(), Length(1, 1000)],
44+
)
45+
no_image_tip = TextAreaField(
46+
"Tips to show when no image",
47+
validators=[InputRequired(), Length(1, 500)],
48+
)
49+
submit = SubmitField("Submit")

manager/fw_manager/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ class User(Base):
6161
password_hash: so.Mapped[str]
6262

6363

64+
class Site(Base):
65+
title: so.Mapped[str]
66+
description: so.Mapped[Optional[str]]
67+
no_image_tip: so.Mapped[Optional[str]]
68+
69+
6470
class Image(Base):
6571
uri: so.Mapped[str] = so.mapped_column(unique=True)
6672
thumbnail_uri: so.Mapped[str]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{% from 'bootstrap5/utils.html' import render_icon %}
2+
{% from 'bootstrap5/form.html' import render_form %}
3+
{% from 'bootstrap5/pagination.html' import render_pagination %}
4+
{% from 'bootstrap5/nav.html' import render_nav_item %}
5+
<!doctype html>
6+
<html lang="en">
7+
8+
<head>
9+
{% block head %}
10+
<!-- Required meta tags -->
11+
<meta charset="utf-8">
12+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
13+
14+
{% block styles %}
15+
<!-- Bootstrap CSS -->
16+
{{ bootstrap.load_css() }}
17+
<style>
18+
html {
19+
height: 100%;
20+
}
21+
22+
body {
23+
min-height: 100%;
24+
}
25+
26+
.img-thumbnail {
27+
height: 50px;
28+
}
29+
30+
.toast-header .badge {
31+
width: 16px;
32+
height: 16px;
33+
}
34+
</style>
35+
{% endblock %}
36+
37+
<title>{% block title %}FineWeather Manager{% endblock %}</title>
38+
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.svg') }}">
39+
{% endblock %}
40+
</head>
41+
42+
<body class="bg-light d-flex flex-column">
43+
<div class="container-lg mt-3 mb-4">
44+
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #4c1d9575;">
45+
<div class="container-fluid">
46+
<a class="navbar-brand" href="#">FineWeather Manager</a>
47+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
48+
<span class="navbar-toggler-icon"></span>
49+
</button>
50+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
51+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
52+
{{ render_nav_item('manager.images_page', 'Home') }}
53+
{{ render_nav_item('manager.settings_page', 'Settings') }}
54+
</ul>
55+
</div>
56+
</div>
57+
</nav>
58+
</div>
59+
{% block content %}
60+
{% endblock %}
61+
62+
{% block scripts %}
63+
<!-- Optional JavaScript -->
64+
{{ bootstrap.load_js() }}
65+
{% endblock %}
66+
</body>
67+
68+
<footer
69+
class="d-flex flex-wrap justify-content-between align-items-center py-3 my-2 flex-grow-0">
70+
<div class="m-auto">
71+
<p class="text-muted">
72+
&copy; {{ year }} CodeKitchen Community
73+
</p>
74+
</div>
75+
</footer>
76+
77+
</html>

0 commit comments

Comments
 (0)