From dfea8eff48b6ea181a51315d7510725007542491 Mon Sep 17 00:00:00 2001 From: pi3c Date: Sat, 7 Oct 2023 21:44:33 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D0=BA=D1=83=20html=20=D0=B8=D0=B7=20=D1=84?= =?UTF-8?q?=D1=83=D1=82=D0=B5=D1=80=D0=B0=20=D0=B2=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=84=D0=B8=D0=B3,=20=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=8E=20=D0=B8=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=BD=D1=8B=D0=B5=20=D0=BB=D0=B8=D0=BD=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D0=B0=D0=BF=D0=BF.=D0=BA=D0=BE=D0=BD=D1=84=D0=B8?= =?UTF-8?q?=D0=B3.=20=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20title=20=D0=B2=20=D0=BF=D0=BE=D1=81=D1=82=D0=B0=D1=85?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=B5=D0=B7=20=D1=82?= =?UTF-8?q?=D1=8D=D0=B3=D0=BE=D0=B2=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- migrations/versions/ea0fde3014d7_.py | 36 +++++++++++ pyproger/admin/views.py | 13 ++++ pyproger/app.py | 31 +++++++--- pyproger/blog/urls.py | 87 ++++++++++++++------------- pyproger/config.py | 23 +++++-- pyproger/dbase/database.py | 21 ++++--- pyproger/dbase/models.py | 15 +++++ pyproger/templates/blog/base.html | 51 ++++------------ pyproger/templates/blog/postview.html | 2 +- 9 files changed, 172 insertions(+), 107 deletions(-) create mode 100644 migrations/versions/ea0fde3014d7_.py diff --git a/migrations/versions/ea0fde3014d7_.py b/migrations/versions/ea0fde3014d7_.py new file mode 100644 index 0000000..81abf41 --- /dev/null +++ b/migrations/versions/ea0fde3014d7_.py @@ -0,0 +1,36 @@ +"""empty message + +Revision ID: ea0fde3014d7 +Revises: 5e6d181b4b74 +Create Date: 2023-10-07 14:32:08.410786 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ea0fde3014d7' +down_revision = '5e6d181b4b74' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('site_headers', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=20), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('content', sa.Text(), nullable=True), + sa.Column('enabled', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('site_headers') + # ### end Alembic commands ### diff --git a/pyproger/admin/views.py b/pyproger/admin/views.py index 077ebbe..c3ec0c4 100644 --- a/pyproger/admin/views.py +++ b/pyproger/admin/views.py @@ -99,3 +99,16 @@ class PageView(MyAdminView): form_overrides = dict(text=CKEditorField) create_template = "admin/edit.html" edit_template = "admin/edit.html" + + +class HeadersView(MyAdminView): + column_labels = dict( + name="Название", + description="Описание содержимого", + content="Содержимое включаемое в header", + enabled="Включить код в страницы сайта", + ) + column_list = ( + "name", + "description", + ) diff --git a/pyproger/app.py b/pyproger/app.py index 1fa3c4e..5cb81e7 100644 --- a/pyproger/app.py +++ b/pyproger/app.py @@ -8,7 +8,8 @@ from flask_migrate import Migrate from flask_security.core import Security from pyproger.dbase import Role, User, db, user_datastore -from pyproger.dbase.models import Page, Post, Tag +from pyproger.dbase.database import get_headers, get_menu_items +from pyproger.dbase.models import Page, Post, SiteHeaders, Tag def create_app(test_config=None): @@ -21,11 +22,7 @@ def create_app(test_config=None): load_dotenv(dotenv_path) app.config["SECRET_KEY"] = os.getenv("SECRET_KEY") app.config["SECURITY_PASSWORD_SALT"] = os.getenv("SECURITY_PASSWORD_SALT") - if os.getenv("SQLALCHEMY_DATABASE_URI") is not None: - app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv( - "SQLALCHEMY_DATABASE_URI" - ) - + app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("SQLALCHEMY_DATABASE_URI") else: app.config.from_mapping(test_config) @@ -50,8 +47,8 @@ def create_app(test_config=None): admin.init_app(app) - from pyproger.admin.views import (PageView, PostView, RoleView, TagView, - UserView) + from pyproger.admin.views import (HeadersView, PageView, PostView, + RoleView, TagView, UserView) admin.add_view( RoleView( @@ -89,8 +86,16 @@ def create_app(test_config=None): PageView( Page, db.session, - category=" Страницы", - name="Страницы блога", + category="Страницы", + name="Статические страницы блога", + ) + ) + admin.add_view( + HeadersView( + SiteHeaders, + db.session, + category="Страницы", + name="Включаемые в html заголовки", ) ) @@ -104,6 +109,12 @@ def create_app(test_config=None): app.register_blueprint(bp_errors) app.register_blueprint(bp_robots) + with app.app_context(): + site_headers = get_headers() + app.config["SITE_HEADERS"] = site_headers + menu_items = get_menu_items() + app.config["MENU_ITEMS"] = menu_items + @security.context_processor def security_context_processor(): return dict( diff --git a/pyproger/blog/urls.py b/pyproger/blog/urls.py index ce6e3ed..0d2f94a 100644 --- a/pyproger/blog/urls.py +++ b/pyproger/blog/urls.py @@ -1,23 +1,11 @@ import locale +import re -from flask import ( - abort, - current_app, - redirect, - render_template, - request, - session, - url_for, -) +from flask import (abort, current_app, redirect, render_template, request, + session, url_for) -from ..dbase.database import ( - get_all_posts_by_tag, - get_menu_items, - get_page, - get_paginated_posts, - get_post, - get_tags, -) +from ..dbase.database import (get_all_posts_by_tag, get_page, + get_paginated_posts, get_post, get_tags) from .blog import bp locale.setlocale(locale.LC_ALL, "") @@ -33,37 +21,46 @@ def index(page=1): list_pages = [ x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2 ] - menu_items = get_menu_items() return render_template( "blog/index.html", - title="pyproger - разговоры про питон", - menu_title="pyproger", - menu_items=menu_items, + title=f'{current_app.config.get("BRAND")} - разговоры про питон', + headers=current_app.config.get("SITE_HEADERS"), + menu_title=current_app.config.get("BRAND"), + menu_items=current_app.config.get("MENU_ITEMS"), posts=posts, page=page, total_pages=total_pages, list_pages=list_pages, + mylinks=current_app.config.get("MYLINKS"), + copyright=current_app.config.get("COPYRIGHT"), ) @bp.route("/post/") @bp.route("/post/") def post(slug=None): - back_url = session.get("back_url") if slug is not None: current_post = get_post(slug) if current_post is None: return abort(404) - menu_items = get_menu_items() + + TAG_RE = re.compile(r"<[^>]+>") + + def remove_tags(text): + return TAG_RE.sub("", text) + + title = remove_tags(current_post.Post.title) return render_template( "blog/postview.html", - title=f"pyproger - {current_post.Post.title}", - menu_title="pyproger", - menu_items=menu_items, + title=f'{current_app.config.get("BRAND")} - {title}', + headers=current_app.config.get("SITE_HEADERS"), + menu_title=current_app.config.get("BRAND"), + menu_items=current_app.config.get("MENU_ITEMS"), post=current_post, - back_url=back_url, + mylinks=current_app.config.get("MYLINKS"), + copyright=current_app.config.get("COPYRIGHT"), ) else: abort(404) @@ -72,13 +69,15 @@ def post(slug=None): @bp.route("/tags/") def get_all_tags(): tags = get_tags() - menu_items = get_menu_items() return render_template( "blog/tags.html", - title="pyproger - поиск по тэгу", - menu_title="pyproger", + title=f'{current_app.config.get("BRAND")} - поиск по тэгу', + headers=current_app.config.get("SITE_HEADERS"), + menu_title=current_app.config.get("BRAND"), tags=tags, - menu_items=menu_items, + menu_items=current_app.config.get("MENU_ITEMS"), + mylinks=current_app.config.get("MYLINKS"), + copyright=current_app.config.get("COPYRIGHT"), ) @@ -89,25 +88,29 @@ def get_posts_by_tag(page=1, tag=None): tag = request.args.get("tag") if tag is None: return redirect(url_for(".get_all_tags")) - per_page = current_app.config.get("POSTS_ON_PAGE") - posts, total_pages = get_all_posts_by_tag(tag, page, per_page) + per_page = current_app.config.get("POSTS_ON_PAGE") + posts, total = get_all_posts_by_tag(tag, page, per_page) + total_pages = total // per_page + [0, 1][total % per_page != 0] + if posts is None: abort(404) list_pages = [ x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2 ] - menu_items = get_menu_items() return render_template( "blog/index.html", - title=f"pyproger - посты по {tag}", - menu_title="pyproger", - menu_items=menu_items, + title=f'{current_app.config.get("BRAND")} - посты по {tag}', + headers=current_app.config.get("SITE_HEADERS"), + menu_title=current_app.config.get("BRAND"), + menu_items=current_app.config.get("MENU_ITEMS"), posts=posts, page=page, total_pages=total_pages, list_pages=list_pages, + mylinks=current_app.config.get("MYLINKS"), + copyright=current_app.config.get("COPYRIGHT"), ) @@ -116,11 +119,13 @@ def page(slug=None): page = get_page(slug) if page is None: abort(404) - menu_items = get_menu_items() return render_template( "blog/page.html", - title=f"pyproger - {page.name}", - menu_title="pyproger", - menu_items=menu_items, + title=f'{current_app.config.get("BRAND")} - {page.name}', + headers=current_app.config.get("SITE_HEADERS"), + menu_title=current_app.config.get("BRAND"), + menu_items=current_app.config.get("MENU_ITEMS"), content_body=page.text, + mylinks=current_app.config.get("MYLINKS"), + copyright=current_app.config.get("COPYRIGHT"), ) diff --git a/pyproger/config.py b/pyproger/config.py index 382b1c3..d81d3bd 100755 --- a/pyproger/config.py +++ b/pyproger/config.py @@ -1,14 +1,28 @@ import os +# Настройки блога +BRAND = "pyproger" +COPYRIGHT = { + "year": "2023", + "name": "Сергей Вaнюшкин", + "link": "https://pi3c.ru", + "city": "г.Нарьян-Мар, Ненецкий А.О.", +} +MYLINKS = ( + {"icon": "fab fa-telegram", "link": "https://t.me/pi3c_nao"}, + {"icon": "fab fa-vk", "link": "https://m.vk.com/pi3c_nao"}, + {"icon": "fab fa-yandex", "link": "mailto:pi3c@yandex.ru"}, + {"icon": "fab fa-github", "link": "https://github.com/pi3c"}, + {"icon": "fa fa-gitea", "link": "https://git.pi3c.ru"}, +) +POSTS_ON_PAGE = 6 + # Тема оформления админ панели FLASK_ADMIN_SWATCH = "slate" # Тестовый ключ SECRET_KEY = "Test_secret_key" -# Настройки подключения к бд -SQLALCHEMY_DATABASE_URI = "sqlite:///sqlite.db" -# For debug - show every DB query SQLALCHEMY_ECHO = False # Настройки Flask-Security @@ -44,6 +58,3 @@ CKEDITOR_PKG_TYPE = "full" CKEDITOR_SERVE_LOCAL = True CKEDITOR_ENABLE_CODESNIPPET = True CKEDITOR_CODE_THEME = "monokai_sublime" - -# Настройки блога -POSTS_ON_PAGE = 6 diff --git a/pyproger/dbase/database.py b/pyproger/dbase/database.py index 9582057..4d84833 100644 --- a/pyproger/dbase/database.py +++ b/pyproger/dbase/database.py @@ -1,9 +1,5 @@ -from datetime import datetime, timezone - -from sqlalchemy import func - from . import db -from .models import Page, Post, Tag, User +from .models import Page, Post, SiteHeaders, Tag, User def get_paginated_posts(page, per_page): @@ -13,11 +9,7 @@ def get_paginated_posts(page, per_page): .order_by(Post.create_datetime.desc()) .paginate(page=page, per_page=per_page, error_out=True) ) - total_pages = ( - all_post_query.total // per_page + [0, 1][all_post_query.total % per_page != 0] - ) - - return all_post_query, total_pages + return all_post_query, all_post_query.total def get_post(slug): @@ -71,3 +63,12 @@ def get_posts_for_sitemap(): .all() ) return posts + + +def get_headers(): + headers = ( + db.session.query(SiteHeaders.content) + .filter(SiteHeaders.enabled.is_(True)) + .all() + ) + return headers diff --git a/pyproger/dbase/models.py b/pyproger/dbase/models.py index 218d336..c8866b7 100644 --- a/pyproger/dbase/models.py +++ b/pyproger/dbase/models.py @@ -107,3 +107,18 @@ class Page(db.Model): default=func.now(), onupdate=func.now(), ) + + +class SiteHeaders(db.Model): + __tablename__ = "site_headers" + id = Column( + Integer, + primary_key=True, + nullable=False, + unique=True, + autoincrement=True, + ) + name = Column(String(20)) + description = Column(Text) + content = Column(Text) + enabled = Column(Boolean, default=False) diff --git a/pyproger/templates/blog/base.html b/pyproger/templates/blog/base.html index a435a5d..25b3193 100644 --- a/pyproger/templates/blog/base.html +++ b/pyproger/templates/blog/base.html @@ -1,14 +1,14 @@ - + - - {% block title %} - {% endblock title %} - + {% block title %}{% endblock title %} + {% for h in headers %} + {{h.content | safe}} + {% endfor %} @@ -67,40 +67,13 @@
- + {% for l in mylinks %} - - - - - - - - - - - - + {% endfor %}
@@ -168,10 +141,10 @@ - © 2023 - Сергей Ванюшкин + © {{ copyright.year}} + {{ copyright.name}}
- г.Нарьян-Мар Ненецкий А.О + {{copyright.city}}
diff --git a/pyproger/templates/blog/postview.html b/pyproger/templates/blog/postview.html index 53e1ed9..8b42fcb 100644 --- a/pyproger/templates/blog/postview.html +++ b/pyproger/templates/blog/postview.html @@ -1,7 +1,7 @@ {% extends 'blog/base.html' %} {% block title %} - {{title}} + {{ title }} {% endblock %} {% block menu_title %}