platzky 1.2.0__tar.gz → 1.2.2__tar.gz
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-1.2.0 → platzky-1.2.2}/PKG-INFO +1 -1
- {platzky-1.2.0 → platzky-1.2.2}/platzky/models.py +19 -9
- {platzky-1.2.0 → platzky-1.2.2}/platzky/seo/seo.py +3 -2
- platzky-1.2.2/platzky/static/styles.css +174 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/base.html +6 -7
- {platzky-1.2.0 → platzky-1.2.2}/pyproject.toml +1 -1
- platzky-1.2.0/platzky/static/styles.css +0 -60
- {platzky-1.2.0 → platzky-1.2.2}/LICENSE +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/README.md +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/__init__.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/admin/admin.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/admin/fake_login.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/admin/templates/admin.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/admin/templates/login.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/admin/templates/module.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/blog/__init__.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/blog/blog.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/blog/comment_form.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/config.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/README.md +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/__init__.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/db_loader.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/github_json_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/google_json_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/graph_ql_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/json_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/json_file_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/db/mongodb_db.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/engine.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/locale/en/LC_MESSAGES/messages.po +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/locale/pl/LC_MESSAGES/messages.po +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/platzky.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/plugin/plugin.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/plugin/plugin_loader.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/static/blog.css +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/telemetry.py +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/404.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/blog.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/body_meta.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/dynamic_css.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/feed.xml +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/head_meta.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/page.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/post.html +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/robots.txt +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/templates/sitemap.xml +0 -0
- {platzky-1.2.0 → platzky-1.2.2}/platzky/www_handler.py +0 -0
|
@@ -160,29 +160,32 @@ class Post(BaseModel):
|
|
|
160
160
|
slug: URL-friendly unique identifier for the post
|
|
161
161
|
title: Post title
|
|
162
162
|
contentInMarkdown: Post content in Markdown format
|
|
163
|
-
comments: List of comments on this post
|
|
164
163
|
excerpt: Short summary or preview of the post
|
|
165
|
-
tags: List of tags for categorization
|
|
166
|
-
language: Language code for the post content
|
|
167
164
|
coverImage: Cover image for the post
|
|
168
|
-
|
|
165
|
+
language: Language code for the post content (defaults to 'en')
|
|
166
|
+
comments: Optional list of comments on this post
|
|
167
|
+
tags: Optional list of tags for categorization
|
|
168
|
+
date: Optional datetime when the post was published (timezone-aware recommended)
|
|
169
169
|
"""
|
|
170
170
|
|
|
171
171
|
author: str
|
|
172
172
|
slug: str
|
|
173
173
|
title: str
|
|
174
174
|
contentInMarkdown: str
|
|
175
|
-
comments: list[Comment]
|
|
176
175
|
excerpt: str
|
|
177
|
-
|
|
178
|
-
language: str
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
coverImage: Image = Field(default_factory=Image)
|
|
177
|
+
language: str = "en"
|
|
178
|
+
comments: list[Comment] = Field(default_factory=list)
|
|
179
|
+
tags: list[str] = Field(default_factory=list)
|
|
180
|
+
date: DateTimeField | None = None
|
|
181
181
|
|
|
182
182
|
def __lt__(self, other: object) -> bool:
|
|
183
183
|
"""Compare posts by date for sorting.
|
|
184
184
|
|
|
185
185
|
Uses datetime comparison to ensure robust and correct ordering.
|
|
186
|
+
Posts without dates are treated as "less than" dated posts, meaning they
|
|
187
|
+
appear last when using descending sort (reverse=True, newest-first) and
|
|
188
|
+
first when using ascending sort.
|
|
186
189
|
|
|
187
190
|
Args:
|
|
188
191
|
other: Another Post instance to compare against
|
|
@@ -192,6 +195,13 @@ class Post(BaseModel):
|
|
|
192
195
|
or NotImplemented if comparing with a non-Post object
|
|
193
196
|
"""
|
|
194
197
|
if isinstance(other, Post):
|
|
198
|
+
# Posts without dates are sorted last
|
|
199
|
+
if self.date is None and other.date is None:
|
|
200
|
+
return False
|
|
201
|
+
if self.date is None:
|
|
202
|
+
return True # None is "less than" any date (sorted last)
|
|
203
|
+
if other.date is None:
|
|
204
|
+
return False
|
|
195
205
|
return self.date < other.date
|
|
196
206
|
return NotImplemented
|
|
197
207
|
|
|
@@ -59,8 +59,9 @@ def create_seo_blueprint(
|
|
|
59
59
|
# TODO: Add get_list_of_posts for faster getting just list of it
|
|
60
60
|
for post in db.get_all_posts(lang):
|
|
61
61
|
slug = post.slug
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
url: dict[str, str] = {"loc": f"{host_base}{blog_prefix}/{slug}"}
|
|
63
|
+
if post.date is not None:
|
|
64
|
+
url["lastmod"] = post.date.date().isoformat()
|
|
64
65
|
dynamic_urls.append(url)
|
|
65
66
|
return dynamic_urls
|
|
66
67
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
html {
|
|
2
|
+
height: 100%;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
body {
|
|
7
|
+
height: 100%;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Prevent page-level scrollbar */
|
|
12
|
+
.container-fluid {
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
max-height: 100vh;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.logo {
|
|
18
|
+
max-height: 6rem;
|
|
19
|
+
max-width: 50vw;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#main-row {
|
|
23
|
+
position: relative;
|
|
24
|
+
flex-grow: 1;
|
|
25
|
+
z-index: 0;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Main content area scrolling */
|
|
30
|
+
main {
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
min-height: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Constrain left panel column to main-row height */
|
|
36
|
+
.col-auto.left-panel {
|
|
37
|
+
max-height: 100%;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.left-panel-contents {
|
|
42
|
+
padding: 0 16px 16px 16px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Panel body scrolling */
|
|
46
|
+
#left-panel .offcanvas-body {
|
|
47
|
+
padding-top: 0;
|
|
48
|
+
overflow-y: auto;
|
|
49
|
+
flex: 1;
|
|
50
|
+
min-height: 0;
|
|
51
|
+
scrollbar-width: thin;
|
|
52
|
+
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Panel header with close button - consistent spacing */
|
|
56
|
+
#left-panel .offcanvas-header {
|
|
57
|
+
padding: 8px 16px;
|
|
58
|
+
min-height: auto;
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Modern close button */
|
|
63
|
+
#left-panel .btn-close {
|
|
64
|
+
width: 32px;
|
|
65
|
+
height: 32px;
|
|
66
|
+
padding: 0;
|
|
67
|
+
background-color: rgba(255, 255, 255, 0.15);
|
|
68
|
+
border-radius: 50%;
|
|
69
|
+
opacity: 1;
|
|
70
|
+
transition: background-color 0.2s ease, transform 0.2s ease;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#left-panel .btn-close:hover {
|
|
74
|
+
background-color: rgba(255, 255, 255, 0.25);
|
|
75
|
+
transform: scale(1.05);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#left-panel .btn-close:focus {
|
|
79
|
+
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Custom scrollbar for panel body (webkit) */
|
|
83
|
+
#left-panel .offcanvas-body::-webkit-scrollbar {
|
|
84
|
+
width: 6px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#left-panel .offcanvas-body::-webkit-scrollbar-track {
|
|
88
|
+
background: transparent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#left-panel .offcanvas-body::-webkit-scrollbar-thumb {
|
|
92
|
+
background-color: rgba(255, 255, 255, 0.3);
|
|
93
|
+
border-radius: 3px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#left-panel .offcanvas-body::-webkit-scrollbar-thumb:hover {
|
|
97
|
+
background-color: rgba(255, 255, 255, 0.5);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Filter category groups */
|
|
101
|
+
#filter-form form > div {
|
|
102
|
+
margin-bottom: 20px;
|
|
103
|
+
padding-bottom: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Filter category header container */
|
|
107
|
+
#filter-form form > div > div:first-child {
|
|
108
|
+
margin-bottom: 10px;
|
|
109
|
+
padding-bottom: 6px;
|
|
110
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Filter category title */
|
|
114
|
+
#filter-form form > div > div:first-child > span {
|
|
115
|
+
font-size: 0.75rem;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.5px;
|
|
119
|
+
opacity: 0.85;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Filter checkbox labels */
|
|
123
|
+
#filter-form form > div > label {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
padding: 6px 8px;
|
|
127
|
+
margin: 0 -8px;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
transition: background-color 0.15s ease;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#filter-form form > div > label:hover {
|
|
134
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@media only screen and (min-width: 992px) {
|
|
138
|
+
#left-panel {
|
|
139
|
+
position: relative;
|
|
140
|
+
inline-size: 220px;
|
|
141
|
+
max-width: 220px;
|
|
142
|
+
width: 220px;
|
|
143
|
+
display: flex;
|
|
144
|
+
flex-direction: column;
|
|
145
|
+
max-height: 100%;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Add some top padding on desktop to match mobile visual balance */
|
|
149
|
+
#left-panel .offcanvas-body {
|
|
150
|
+
padding-top: 8px;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.language-indicator-text {
|
|
155
|
+
color: white;
|
|
156
|
+
font-weight: normal;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.offcanvas-lg.offcanvas-start {
|
|
160
|
+
top: auto;
|
|
161
|
+
width: auto;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.dropdown-menu {
|
|
165
|
+
z-index: 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.btn {
|
|
169
|
+
padding: 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
i.fi {
|
|
173
|
+
margin: 0 0.5em;
|
|
174
|
+
}
|
|
@@ -71,22 +71,21 @@
|
|
|
71
71
|
|
|
72
72
|
<div class="row g-0" id="main-row">
|
|
73
73
|
{% if self.left_panel() %}
|
|
74
|
-
<div class="col-
|
|
74
|
+
<div class="col-auto left-panel" >
|
|
75
75
|
<div id="left-panel" class="left-panel offcanvas-lg offcanvas-start" aria-modal="true" role="dialog">
|
|
76
|
-
<div class="offcanvas-
|
|
76
|
+
<div class="offcanvas-header d-lg-none justify-content-end">
|
|
77
|
+
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" data-bs-target="#left-panel" aria-label="{{ _('Close left panel') }}"></button>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="offcanvas-body">
|
|
77
80
|
<div class="left-panel-contents">
|
|
78
81
|
{% block left_panel %}{% endblock %}
|
|
79
82
|
</div>
|
|
80
|
-
<div class="offcanvas-header justify-content-between align-items-start">
|
|
81
|
-
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" data-bs-target="#left-panel" ></button>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
83
|
</div>
|
|
85
84
|
</div>
|
|
86
85
|
|
|
87
86
|
</div>
|
|
88
87
|
{% endif %}
|
|
89
|
-
<div class="h-100 {% if self.left_panel() %} col
|
|
88
|
+
<div class="h-100 {% if self.left_panel() %} col {% else %} col-lg-12 {% endif %}">
|
|
90
89
|
<main class="h-100">
|
|
91
90
|
{% block content %}{% endblock %}
|
|
92
91
|
</main>
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
html,
|
|
2
|
-
body {
|
|
3
|
-
height: 100%;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
.logo {
|
|
7
|
-
max-height: 6rem;
|
|
8
|
-
max-width: 50vw;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
#main-row {
|
|
12
|
-
position: relative;
|
|
13
|
-
flex-grow: 1;
|
|
14
|
-
z-index: 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.left-panel-contents {
|
|
18
|
-
padding: 16px;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@media only screen and (min-width: 768px) {
|
|
22
|
-
#left-panel {
|
|
23
|
-
inline-size: calc(100vw * 0.1666666667);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.language-indicator-text {
|
|
28
|
-
color: white;
|
|
29
|
-
font-weight: normal;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.offcanvas-lg.offcanvas-start {
|
|
33
|
-
top: auto;
|
|
34
|
-
width: auto;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
#filter-form form > div {
|
|
38
|
-
padding-bottom: 1rem;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
#filter-form form div > span {
|
|
42
|
-
font-weight: bold;
|
|
43
|
-
padding-bottom: 0.5rem;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.dropdown-menu {
|
|
47
|
-
z-index: 1;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.btn {
|
|
51
|
-
padding: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.btn-close {
|
|
55
|
-
opacity: 1;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
i.fi {
|
|
59
|
-
margin: 0 0.5em;
|
|
60
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|