Вынес статику html из футера в конфиг, Вынес меню и приватные линки в апп.конфиг. Поправил отображение title в постах для отображения без тэгов разметки
parent
19dec390c3
commit
dfea8eff48
|
@ -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 ###
|
|
@ -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",
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
@ -90,7 +87,15 @@ def create_app(test_config=None):
|
|||
Page,
|
||||
db.session,
|
||||
category="Страницы",
|
||||
name="Страницы блога",
|
||||
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(
|
||||
|
|
|
@ -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/<path:slug>")
|
||||
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"),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="{{page_lang}}">
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<title>
|
||||
{% block title %}
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
{% for h in headers %}
|
||||
{{h.content | safe}}
|
||||
{% endfor %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<!-- Font Awesome -->
|
||||
|
@ -67,40 +67,13 @@
|
|||
<div class="container p-2">
|
||||
<!-- Section: Social media -->
|
||||
<section class="mb-2">
|
||||
<!-- Telegram -->
|
||||
{% for l in mylinks %}
|
||||
<a class="btn btn-outline-light btn-floating m-2"
|
||||
href="https://t.me/pi3c_nao"
|
||||
href="{{ l.link }}"
|
||||
role="button"
|
||||
><i class="fab fa-telegram"></i
|
||||
></a>
|
||||
|
||||
<!-- VK -->
|
||||
<a class="btn btn-outline-light btn-floating m-2"
|
||||
href="https://m.vk.com/pi3c_nao"
|
||||
role="button"
|
||||
><i class="fab fa-vk"></i
|
||||
></a>
|
||||
|
||||
<!-- Yandex -->
|
||||
<a class="btn btn-outline-light btn-floating m-2"
|
||||
href="mailto:pi3c@yandex.ru"
|
||||
role="button"
|
||||
><i class="fab fa-yandex"></i
|
||||
></a>
|
||||
|
||||
<!-- Github -->
|
||||
<a class="btn btn-outline-light btn-floating m-2"
|
||||
href="https://github.com/pi3c"
|
||||
role="button"
|
||||
><i class="fab fa-github"></i
|
||||
></a>
|
||||
|
||||
<!-- Gitea -->
|
||||
<a class="btn btn-outline-light btn-floating m-2"
|
||||
href="https://git.pi3c.ru"
|
||||
role="button"
|
||||
><i class="fa fa-gitea"></i
|
||||
><i class="{{ l.icon }}"></i
|
||||
></a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
<!-- Section: Social media -->
|
||||
|
||||
|
@ -168,10 +141,10 @@
|
|||
|
||||
<!-- Copyright -->
|
||||
<small class="text-center text-body">
|
||||
© 2023
|
||||
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" href="https://pi3c.ru/">Сергей Ванюшкин</a>
|
||||
© {{ copyright.year}}
|
||||
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" href="{{ copyright.link }}">{{ copyright.name}}</a>
|
||||
<br>
|
||||
г.Нарьян-Мар Ненецкий А.О
|
||||
{{copyright.city}}
|
||||
</small>
|
||||
<!-- Copyright -->
|
||||
</footer>
|
||||
|
|
Loading…
Reference in New Issue