platzky 0.1.19__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- platzky/blog/__init__.py +0 -0
- platzky/blog/blog.py +61 -35
- platzky/blog/comment_form.py +8 -4
- platzky/config.py +52 -49
- platzky/db/__init__.py +0 -0
- platzky/db/db.py +99 -0
- platzky/db/db_loader.py +32 -0
- platzky/db/google_json_db.py +33 -10
- platzky/db/graph_ql_db.py +113 -57
- platzky/db/json_db.py +81 -23
- platzky/db/json_file_db.py +25 -9
- platzky/models.py +64 -0
- platzky/platzky.py +122 -62
- platzky/plugin_loader.py +31 -34
- platzky/plugins/redirections/entrypoint.py +41 -19
- platzky/plugins/sendmail/entrypoint.py +31 -10
- platzky/seo/seo.py +34 -24
- platzky/static/blog.css +5 -12
- platzky/templates/base.html +137 -22
- platzky/templates/blog.html +3 -2
- platzky/templates/body_meta.html +4 -21
- platzky/templates/feed.xml +1 -1
- platzky/templates/head_meta.html +5 -15
- platzky/templates/post.html +2 -2
- platzky/www_handler.py +7 -4
- platzky-0.2.1.dist-info/METADATA +40 -0
- platzky-0.2.1.dist-info/RECORD +34 -0
- {platzky-0.1.19.dist-info → platzky-0.2.1.dist-info}/WHEEL +1 -1
- platzky/blog/db.py +0 -18
- platzky/blog/post_formatter.py +0 -16
- platzky/db_loader.py +0 -11
- platzky-0.1.19.dist-info/METADATA +0 -43
- platzky-0.1.19.dist-info/RECORD +0 -32
platzky/platzky.py
CHANGED
|
@@ -1,94 +1,154 @@
|
|
|
1
|
-
|
|
1
|
+
import typing as t
|
|
2
|
+
import urllib.parse
|
|
3
|
+
|
|
4
|
+
from flask import Flask, redirect, render_template, request, session
|
|
2
5
|
from flask_babel import Babel
|
|
3
6
|
from flask_minify import Minify
|
|
4
7
|
|
|
5
|
-
import os
|
|
6
|
-
import urllib.parse
|
|
7
|
-
|
|
8
|
-
from . import config, db_loader
|
|
9
8
|
from .blog import blog
|
|
9
|
+
from .config import (
|
|
10
|
+
Config,
|
|
11
|
+
languages_dict,
|
|
12
|
+
)
|
|
13
|
+
from .db.db_loader import get_db
|
|
10
14
|
from .plugin_loader import plugify
|
|
11
15
|
from .seo import seo
|
|
12
|
-
from .www_handler import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
16
|
+
from .www_handler import redirect_nonwww_to_www, redirect_www_to_nonwww
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Engine(Flask):
|
|
20
|
+
def __init__(self, config: Config, db, import_name):
|
|
21
|
+
super().__init__(import_name)
|
|
22
|
+
self.config.from_mapping(config.model_dump(by_alias=True))
|
|
23
|
+
self.db = db
|
|
24
|
+
self.notifiers = []
|
|
25
|
+
self.dynamic_body = ""
|
|
26
|
+
self.dynamic_head = ""
|
|
27
|
+
babel_translation_directories = ";".join(config.translation_directories)
|
|
28
|
+
self.babel = Babel(
|
|
29
|
+
self,
|
|
30
|
+
locale_selector=self.get_locale,
|
|
31
|
+
default_translation_directories=babel_translation_directories,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def notify(self, message: str):
|
|
35
|
+
for notifier in self.notifiers:
|
|
36
|
+
notifier(message)
|
|
37
|
+
|
|
38
|
+
def add_notifier(self, notifier):
|
|
39
|
+
self.notifiers.append(notifier)
|
|
40
|
+
|
|
41
|
+
def add_dynamic_body(self, body: str):
|
|
42
|
+
self.dynamic_body += body
|
|
43
|
+
|
|
44
|
+
def add_dynamic_head(self, body: str):
|
|
45
|
+
self.dynamic_head += body
|
|
46
|
+
|
|
47
|
+
def get_locale(self) -> str:
|
|
48
|
+
domain = request.headers["Host"]
|
|
49
|
+
domain_to_lang = self.config.get("DOMAIN_TO_LANG")
|
|
50
|
+
|
|
51
|
+
languages = self.config.get("LANGUAGES", {}).keys()
|
|
52
|
+
backup_lang = session.get(
|
|
53
|
+
"language",
|
|
54
|
+
request.accept_languages.best_match(languages, "en"),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if domain_to_lang:
|
|
58
|
+
lang = domain_to_lang.get(domain, backup_lang)
|
|
59
|
+
else:
|
|
60
|
+
lang = backup_lang
|
|
41
61
|
|
|
62
|
+
session["language"] = lang
|
|
63
|
+
return lang
|
|
42
64
|
|
|
43
|
-
def create_engine(config, db, languages, domain_langs):
|
|
44
|
-
app = Flask(__name__)
|
|
45
|
-
app.config.from_mapping(config)
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
app
|
|
49
|
-
languages = languages
|
|
50
|
-
domain_langs = domain_langs
|
|
66
|
+
def create_engine(config: Config, db) -> Engine:
|
|
67
|
+
app = Engine(config, db, __name__)
|
|
51
68
|
|
|
52
69
|
@app.before_request
|
|
53
70
|
def handle_www_redirection():
|
|
54
|
-
if
|
|
71
|
+
if config.use_www:
|
|
55
72
|
return redirect_nonwww_to_www()
|
|
56
73
|
else:
|
|
57
74
|
return redirect_www_to_nonwww()
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
request.accept_languages.best_match(languages.keys(), 'en')))
|
|
65
|
-
session['language'] = lang
|
|
66
|
-
return lang
|
|
67
|
-
|
|
68
|
-
def get_langs_domain(lang):
|
|
69
|
-
return languages.get(lang).get('domain')
|
|
76
|
+
def get_langs_domain(lang: str) -> t.Optional[str]:
|
|
77
|
+
lang_cfg = config.languages.get(lang)
|
|
78
|
+
if lang_cfg is None:
|
|
79
|
+
return None
|
|
80
|
+
return lang_cfg.domain
|
|
70
81
|
|
|
71
|
-
@app.route(
|
|
82
|
+
@app.route("/lang/<string:lang>", methods=["GET"])
|
|
72
83
|
def change_language(lang):
|
|
73
84
|
if new_domain := get_langs_domain(lang):
|
|
74
85
|
return redirect("http://" + new_domain, code=301)
|
|
75
86
|
else:
|
|
76
|
-
session[
|
|
87
|
+
session["language"] = lang
|
|
77
88
|
return redirect(request.referrer)
|
|
78
89
|
|
|
90
|
+
@app.route("/logo", methods=["GET"])
|
|
91
|
+
def logo():
|
|
92
|
+
return redirect(
|
|
93
|
+
"https://www.problematy.pl/wp-content/uploads/2023/08/kolor_poziom.png"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def url_link(x: str) -> str:
|
|
97
|
+
return urllib.parse.quote(x, safe="")
|
|
98
|
+
|
|
79
99
|
@app.context_processor
|
|
80
100
|
def utils():
|
|
101
|
+
locale = app.get_locale()
|
|
102
|
+
flag = lang.flag if (lang := config.languages.get(locale)) is not None else ""
|
|
81
103
|
return {
|
|
82
|
-
"app_name":
|
|
83
|
-
|
|
84
|
-
"current_flag":
|
|
85
|
-
"current_language":
|
|
86
|
-
"url_link":
|
|
87
|
-
"menu_items": app.db.get_menu_items()
|
|
104
|
+
"app_name": config.app_name,
|
|
105
|
+
"languages": languages_dict(config.languages),
|
|
106
|
+
"current_flag": flag,
|
|
107
|
+
"current_language": locale,
|
|
108
|
+
"url_link": url_link,
|
|
109
|
+
"menu_items": app.db.get_menu_items(),
|
|
110
|
+
"logo_url": app.db.get_logo_url(),
|
|
111
|
+
"font": app.db.get_font(),
|
|
112
|
+
"primary_color": app.db.get_primary_color(),
|
|
113
|
+
"secondary_color": app.db.get_secondary_color(),
|
|
88
114
|
}
|
|
89
115
|
|
|
116
|
+
@app.context_processor
|
|
117
|
+
def dynamic_body():
|
|
118
|
+
return {"dynamic_body": app.dynamic_body}
|
|
119
|
+
|
|
120
|
+
@app.context_processor
|
|
121
|
+
def dynamic_head():
|
|
122
|
+
return {"dynamic_head": app.dynamic_head}
|
|
123
|
+
|
|
90
124
|
@app.errorhandler(404)
|
|
91
125
|
def page_not_found(e):
|
|
92
|
-
return render_template(
|
|
126
|
+
return render_template("404.html", title="404"), 404
|
|
93
127
|
|
|
94
128
|
return plugify(app)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def create_app_from_config(config: Config) -> Engine:
|
|
132
|
+
engine = create_engine_from_config(config)
|
|
133
|
+
blog_blueprint = blog.create_blog_blueprint(
|
|
134
|
+
db=engine.db,
|
|
135
|
+
blog_prefix=config.blog_prefix,
|
|
136
|
+
locale_func=engine.get_locale,
|
|
137
|
+
)
|
|
138
|
+
seo_blueprint = seo.create_seo_blueprint(db=engine.db, config=engine.config)
|
|
139
|
+
engine.register_blueprint(blog_blueprint)
|
|
140
|
+
engine.register_blueprint(seo_blueprint)
|
|
141
|
+
|
|
142
|
+
Minify(app=engine, html=True, js=True, cssless=True)
|
|
143
|
+
return engine
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def create_engine_from_config(config: Config) -> Engine:
|
|
147
|
+
"""Create an engine from a config."""
|
|
148
|
+
db = get_db(config.db)
|
|
149
|
+
return create_engine(config, db)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def create_app(config_path: str) -> Engine:
|
|
153
|
+
config = Config.parse_yaml(config_path)
|
|
154
|
+
return create_app_from_config(config)
|
platzky/plugin_loader.py
CHANGED
|
@@ -1,42 +1,39 @@
|
|
|
1
|
+
import importlib.util
|
|
1
2
|
import os
|
|
2
3
|
import sys
|
|
3
|
-
from os.path import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
from os.path import abspath, dirname
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def find_plugin(plugin_name):
|
|
8
|
+
"""Find plugin by name and return it as module.
|
|
9
|
+
:param plugin_name: name of plugin to find
|
|
10
|
+
:return: module of plugin
|
|
11
|
+
"""
|
|
12
|
+
plugins_dir = os.path.join(dirname(abspath(__file__)), "plugins")
|
|
13
|
+
module_name = plugin_name.removesuffix(".py")
|
|
14
|
+
spec = importlib.util.spec_from_file_location(
|
|
15
|
+
module_name, os.path.join(plugins_dir, plugin_name, "entrypoint.py")
|
|
16
|
+
)
|
|
17
|
+
assert spec is not None
|
|
18
|
+
plugin = importlib.util.module_from_spec(spec)
|
|
19
|
+
sys.modules[module_name] = plugin
|
|
20
|
+
assert spec.loader is not None
|
|
21
|
+
spec.loader.exec_module(plugin)
|
|
22
|
+
return plugin
|
|
13
23
|
|
|
14
24
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
raise Exception(f"Plugins {selected_not_enabled} has been selected in config, but has not been installed.")
|
|
21
|
-
|
|
22
|
-
for plugin_dir in enabled_plugins:
|
|
23
|
-
module_name = plugin_dir.removesuffix('.py')
|
|
24
|
-
spec = importlib.util.spec_from_file_location(module_name,
|
|
25
|
-
os.path.join(plugins_dir, plugin_dir, "entrypoint.py"))
|
|
26
|
-
plugin = importlib.util.module_from_spec(spec)
|
|
27
|
-
sys.modules[module_name] = plugin
|
|
28
|
-
spec.loader.exec_module(plugin)
|
|
29
|
-
plugins.append(plugin)
|
|
30
|
-
|
|
31
|
-
for finder, name, ispkg in pkgutil.iter_modules():
|
|
32
|
-
if name.startswith('platzky_'):
|
|
33
|
-
plugins.append(importlib.import_module(name))
|
|
25
|
+
def plugify(app):
|
|
26
|
+
"""Load plugins and run their entrypoints.
|
|
27
|
+
:param app: Flask app
|
|
28
|
+
:return: Flask app
|
|
29
|
+
"""
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
plugins_data = app.db.get_plugins_data()
|
|
36
32
|
|
|
33
|
+
for plugin_data in plugins_data:
|
|
34
|
+
plugin_config = plugin_data["config"]
|
|
35
|
+
plugin_name = plugin_data["name"]
|
|
36
|
+
plugin = find_plugin(plugin_name)
|
|
37
|
+
plugin.process(app, plugin_config)
|
|
37
38
|
|
|
38
|
-
def plugify(app):
|
|
39
|
-
plugins = set(app.config["PLUGINS"])
|
|
40
|
-
for plugin in find_plugins(plugins):
|
|
41
|
-
plugin.process(app)
|
|
42
39
|
return app
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
from flask import redirect
|
|
2
|
-
from functools import partial
|
|
3
2
|
from gql import gql
|
|
3
|
+
from pydantic import BaseModel
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def
|
|
6
|
+
def json_db_get_redirections(self):
|
|
7
7
|
return self.data.get("redirections", {})
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def json_file_db_get_redirections(self):
|
|
11
|
+
return json_db_get_redirections(self)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def google_json_db_get_redirections(self):
|
|
11
15
|
return self.data.get("redirections", {})
|
|
12
16
|
|
|
13
17
|
|
|
14
|
-
def
|
|
18
|
+
def graph_ql_db_get_redirections(self):
|
|
15
19
|
redirections = gql(
|
|
16
20
|
"""
|
|
17
21
|
query MyQuery{
|
|
@@ -22,25 +26,43 @@ def graphql_get_redirections(self):
|
|
|
22
26
|
}
|
|
23
27
|
"""
|
|
24
28
|
)
|
|
25
|
-
return {
|
|
29
|
+
return {
|
|
30
|
+
x["source"]: x["destination"]
|
|
31
|
+
for x in self.client.execute(redirections)["redirections"]
|
|
32
|
+
}
|
|
26
33
|
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"graph_ql": graphql_get_redirections,
|
|
32
|
-
"google_json": google_get_redirections
|
|
35
|
+
class Redirection(BaseModel):
|
|
36
|
+
source: str
|
|
37
|
+
destiny: str
|
|
33
38
|
|
|
34
|
-
}
|
|
35
|
-
return redirections[db_type]
|
|
36
39
|
|
|
40
|
+
def parse_redirections(config: dict) -> list[Redirection]:
|
|
41
|
+
return [
|
|
42
|
+
Redirection(source=source, destiny=destiny)
|
|
43
|
+
for source, destiny in config.items()
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def setup_routes(app, redirections):
|
|
48
|
+
for redirection in redirections:
|
|
49
|
+
func = redirect_with_name(
|
|
50
|
+
redirection.destiny,
|
|
51
|
+
code=301,
|
|
52
|
+
name=f"{redirection.source}-{redirection.destiny}",
|
|
53
|
+
)
|
|
54
|
+
app.route(rule=redirection.source)(func)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def redirect_with_name(destiny, code, name):
|
|
58
|
+
def named_redirect(*args, **kwargs):
|
|
59
|
+
return redirect(destiny, code, *args, **kwargs)
|
|
60
|
+
|
|
61
|
+
named_redirect.__name__ = name
|
|
62
|
+
return named_redirect
|
|
37
63
|
|
|
38
|
-
def process(app):
|
|
39
|
-
app.db.get_redirections = get_proper_redirections(app.config["DB"]["TYPE"])
|
|
40
|
-
redirects = app.db.get_redirections(app.db)
|
|
41
|
-
for source, destiny in redirects.items():
|
|
42
|
-
func = partial(redirect, destiny, code=301)
|
|
43
|
-
func.__name__ = f"{source}-{destiny}"
|
|
44
|
-
app.route(rule=source)(func)
|
|
45
64
|
|
|
65
|
+
def process(app, config: dict) -> object:
|
|
66
|
+
redirections = parse_redirections(config)
|
|
67
|
+
setup_routes(app, redirections)
|
|
46
68
|
return app
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import smtplib
|
|
2
|
-
from functools import partial
|
|
3
2
|
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
-
def send_mail(sender_email, password, smtp_server, port, receiver_email, subject, message):
|
|
6
|
-
full_message = f'From: {sender_email}\nTo: {receiver_email}\nSubject: {subject}\n\n{message}'
|
|
7
5
|
|
|
6
|
+
def send_mail(
|
|
7
|
+
sender_email, password, smtp_server, port, receiver_email, subject, message
|
|
8
|
+
):
|
|
9
|
+
full_message = (
|
|
10
|
+
f"From: {sender_email}\nTo: {receiver_email}\nSubject: {subject}\n\n{message}"
|
|
11
|
+
)
|
|
8
12
|
server = smtplib.SMTP_SSL(smtp_server, port)
|
|
9
13
|
server.ehlo()
|
|
10
14
|
server.login(sender_email, password)
|
|
@@ -12,11 +16,28 @@ def send_mail(sender_email, password, smtp_server, port, receiver_email, subject
|
|
|
12
16
|
server.close()
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
class SendMailConfig(BaseModel):
|
|
20
|
+
user: str = Field(alias="sender_email")
|
|
21
|
+
password: str = Field(alias="password")
|
|
22
|
+
server: str = Field(alias="smtp_server")
|
|
23
|
+
port: int = Field(alias="port")
|
|
24
|
+
receiver: str = Field(alias="receiver_email")
|
|
25
|
+
subject: str = Field(alias="subject")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def process(app, config):
|
|
29
|
+
plugin_config = SendMailConfig.model_validate(config)
|
|
30
|
+
|
|
31
|
+
def notify(message):
|
|
32
|
+
send_mail(
|
|
33
|
+
sender_email=plugin_config.user,
|
|
34
|
+
password=plugin_config.password,
|
|
35
|
+
smtp_server=plugin_config.server,
|
|
36
|
+
port=plugin_config.port,
|
|
37
|
+
receiver_email=plugin_config.receiver,
|
|
38
|
+
subject=plugin_config.subject,
|
|
39
|
+
message=message,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
app.add_notifier(notify)
|
|
22
43
|
return app
|
platzky/seo/seo.py
CHANGED
|
@@ -1,27 +1,34 @@
|
|
|
1
|
+
import typing as t
|
|
1
2
|
import urllib.parse
|
|
2
3
|
from os.path import dirname
|
|
3
|
-
from flask import
|
|
4
|
+
from flask import Blueprint, current_app, make_response, render_template, request
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
def create_seo_blueprint(db, config):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def create_seo_blueprint(db, config: dict[str, t.Any]):
|
|
8
|
+
seo = Blueprint(
|
|
9
|
+
"seo",
|
|
10
|
+
__name__,
|
|
11
|
+
url_prefix=config["SEO_PREFIX"],
|
|
12
|
+
template_folder=f"{dirname(__file__)}/../templates",
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
@seo.route("/robots.txt")
|
|
12
16
|
def robots():
|
|
13
|
-
robots_response = render_template(
|
|
17
|
+
robots_response = render_template(
|
|
18
|
+
"robots.txt", domain=request.host, mimetype="text/plain"
|
|
19
|
+
)
|
|
14
20
|
response = make_response(robots_response)
|
|
15
21
|
response.headers["Content-Type"] = "text/plain"
|
|
16
22
|
return response
|
|
17
23
|
|
|
18
24
|
@seo.route("/sitemap.xml")
|
|
19
25
|
def main_sitemap():
|
|
20
|
-
if domain_to_lang := config
|
|
21
|
-
|
|
22
|
-
return sitemap(domains_lang)
|
|
26
|
+
if domain_to_lang := config["DOMAIN_TO_LANG"]:
|
|
27
|
+
return sitemap(domain_to_lang[request.host])
|
|
23
28
|
else:
|
|
24
|
-
return sitemap(
|
|
29
|
+
return sitemap(
|
|
30
|
+
config.get("TRANSLATION_DIRECTORIES")
|
|
31
|
+
) # TODO should be based on localization not on config
|
|
25
32
|
|
|
26
33
|
def sitemap(lang):
|
|
27
34
|
"""
|
|
@@ -38,28 +45,31 @@ def create_seo_blueprint(db, config):
|
|
|
38
45
|
static_urls = list()
|
|
39
46
|
for rule in current_app.url_map.iter_rules():
|
|
40
47
|
if not str(rule).startswith("/admin") and not str(rule).startswith("/user"):
|
|
41
|
-
if
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
if (
|
|
49
|
+
rule.methods is not None
|
|
50
|
+
and "GET" in rule.methods
|
|
51
|
+
and len(rule.arguments) == 0
|
|
52
|
+
):
|
|
53
|
+
url = {"loc": f"{host_base}{str(rule)}"}
|
|
45
54
|
static_urls.append(url)
|
|
46
55
|
|
|
47
56
|
# Dynamic routes with dynamic content
|
|
48
57
|
dynamic_urls = list()
|
|
49
58
|
seo_posts = db.get_all_posts(lang)
|
|
50
59
|
for post in seo_posts:
|
|
51
|
-
slug = post[
|
|
52
|
-
datet = post[
|
|
53
|
-
url = {
|
|
54
|
-
"loc": f"{host_base}/{slug}",
|
|
55
|
-
"lastmod": datet
|
|
56
|
-
}
|
|
60
|
+
slug = post["slug"]
|
|
61
|
+
datet = post["date"].split("T")[0]
|
|
62
|
+
url = {"loc": f"{host_base}/{slug}", "lastmod": datet}
|
|
57
63
|
dynamic_urls.append(url)
|
|
58
64
|
|
|
59
|
-
statics = list({v[
|
|
60
|
-
dynamics = list({v[
|
|
61
|
-
xml_sitemap = render_template(
|
|
62
|
-
|
|
65
|
+
statics = list({v["loc"]: v for v in static_urls}.values())
|
|
66
|
+
dynamics = list({v["loc"]: v for v in dynamic_urls}.values())
|
|
67
|
+
xml_sitemap = render_template(
|
|
68
|
+
"sitemap.xml",
|
|
69
|
+
static_urls=statics,
|
|
70
|
+
dynamic_urls=dynamics,
|
|
71
|
+
host_base=host_base,
|
|
72
|
+
)
|
|
63
73
|
response = make_response(xml_sitemap)
|
|
64
74
|
response.headers["Content-Type"] = "application/xml"
|
|
65
75
|
return response
|
platzky/static/blog.css
CHANGED
|
@@ -81,29 +81,22 @@ img::-moz-selection {
|
|
|
81
81
|
background: transparent;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
#mainNav {
|
|
85
|
-
# position: absolute;
|
|
86
|
-
# border-bottom: 1px solid #e9ecef;
|
|
87
|
-
# background-color: white;
|
|
88
|
-
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
84
|
#mainNav .navbar-brand {
|
|
92
|
-
font-weight:
|
|
85
|
+
font-weight: 1000;
|
|
93
86
|
color: #343a40;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
#mainNav .navbar-toggler {
|
|
97
|
-
font-size:
|
|
90
|
+
font-size: 20px;
|
|
98
91
|
font-weight: 800;
|
|
99
|
-
padding:
|
|
92
|
+
padding: 10px;
|
|
100
93
|
text-transform: uppercase;
|
|
101
94
|
color: #343a40;
|
|
102
95
|
}
|
|
103
96
|
|
|
104
97
|
#mainNav .navbar-nav > li.nav-item > a {
|
|
105
|
-
font-size:
|
|
106
|
-
font-weight:
|
|
98
|
+
font-size: 15px;
|
|
99
|
+
font-weight: 1000;
|
|
107
100
|
letter-spacing: 1px;
|
|
108
101
|
text-transform: uppercase;
|
|
109
102
|
}
|